/*************************************************************************
* (C) 2011 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
* by Michael Pounov <misho@openbsd-bg.org>
*
* $Author: misho $
* $Id: pty.c,v 1.8 2016/08/18 09:06:31 misho Exp $
*
**************************************************************************
The ELWIX and AITNET software is distributed under the following
terms:
All of the documentation and software included in the ELWIX and AITNET
Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
Copyright 2004 - 2016
by Michael Pounov <misho@elwix.org>. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by Michael Pounov <misho@elwix.org>
ELWIX - Embedded LightWeight unIX and its contributors.
4. Neither the name of AITNET nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
#include "global.h"
/*
* ioAllocPTY() - Allocate new PTY and TTY
*
* @ptyfd = master fd, pty
* @ttyfd = slave fd, tty
* @name = tty device name if not null
* @namesiz = name length, must be above 63 bytes.
* @term = termios for terminal
* @winz = winsize for terminal
* return: -1 error or 0 ok
*/
int
ioAllocPTY(int *ptyfd, int *ttyfd, char * __restrict name, int namesiz,
struct termios * __restrict term, struct winsize * __restrict winz)
{
assert(ptyfd && ttyfd);
if (!ptyfd || !ttyfd || (name && namesiz < 64)) {
io_SetErr(EINVAL, "Invalid arguments");
return -1;
}
memset(name, 0, namesiz);
if (openpty(ptyfd, ttyfd, name, term, winz) == -1) {
LOGERR;
return -1;
}
return 0;
}
/*
* ioFreePTY() - Release PTY and TTY device
*
* @ptyfd = master fd, pty (==-1 skip closing pty)
* @ttyname = tty filename
* return: none
*/
void
ioFreePTY(int ptyfd, const char *ttyname)
{
assert(ttyname);
if (!ttyname)
return;
if (ptyfd != -1)
close(ptyfd);
if (*ttyname) {
chown(ttyname, (uid_t) 0, (gid_t) 0);
chmod(ttyname, (mode_t) 0666);
}
}
/*
* ioChgWinPTY() - Change window size of PTY
*
* @ptyfd = master fd, pty
* @row = row
* @col = col
* @xpxl = x pixels
* @ypxl = y pixels
* return: -1 error or 0 ok
*/
int
ioChgWinPTY(int ptyfd, u_short row, u_short col, u_short xpxl, u_short ypxl)
{
struct winsize w;
w.ws_row = row;
w.ws_col = col;
w.ws_xpixel = xpxl;
w.ws_ypixel = ypxl;
if (ioctl(ptyfd, TIOCSWINSZ, &w) == -1) {
LOGERR;
return -1;
}
return 0;
}
/*
* ioSetOwnerTTY() - Set owner to TTY
*
* @ttyname = tty filename
* @UID = uid
* @GID = gid
* return: -1 error or 0 ok
*/
int
ioSetOwnerTTY(const char *ttyname, uid_t UID, gid_t GID)
{
struct group *grp;
gid_t gid;
mode_t mode;
struct stat st;
assert(ttyname);
if (!ttyname) {
io_SetErr(EINVAL, "Invalid arguments");
return -1;
}
grp = getgrnam("tty");
if (!grp) {
gid = GID;
mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
} else {
gid = grp->gr_gid;
mode = S_IRUSR | S_IWUSR | S_IWGRP;
}
if (stat(ttyname, &st) == -1) {
LOGERR;
return -1;
}
if (st.st_uid != UID || st.st_gid != gid)
if (chown(ttyname, UID, gid) == -1) {
LOGERR;
return -1;
}
if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode)
if (chmod(ttyname, mode) == -1) {
LOGERR;
return -1;
}
return 0;
}
/*
* ioSetSidTTY() - Makes the process's controlling TTY and sets it to sane modes.
*
* @ttyfd = slave fd, tty
* @ttyname = tty filename
* return: -1 error or 0 ok
*/
int
ioSetSidTTY(int *ttyfd, const char *ttyname)
{
int fd;
/* First disconnect from the old controlling tty. */
#ifdef TIOCNOTTY
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
if (fd >= 0) {
ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
#endif
setsid();
/* Verify that we are successfully disconnected from the controlling tty. */
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
if (fd >= 0) {
io_SetErr(ENXIO, "Failed to disconnect from controlling tty");
close(fd);
return -1;
}
/* Make it our controlling tty. */
#ifdef TIOCSCTTY
if (ioctl(*ttyfd, TIOCSCTTY, NULL) == -1) {
LOGERR;
return -1;
}
#endif
fd = open(ttyname, O_RDWR);
if (fd == -1) {
LOGERR;
return -1;
} else
close(fd);
/* Verify that we now have a controlling tty. */
fd = open(_PATH_TTY, O_WRONLY);
if (fd == -1) {
LOGERR;
return -1;
} else
close(fd);
/* redirect standart handles to tty */
dup2(*ttyfd, STDIN_FILENO);
dup2(*ttyfd, STDOUT_FILENO);
dup2(*ttyfd, STDERR_FILENO);
if (*ttyfd > 2)
close(*ttyfd);
/* NOW TTY IS READY! */
return 0;
}
/*
* ioSetRAWMode() - Enter into RAW mode
*
* @fd = tty fd
* @otio = saved old termios for later restore if !=NULL
* return: -1 error or 0 ok
*/
int
ioSetRAWMode(int fd, struct termios *otio)
{
struct termios tio;
if (tcgetattr(fd, &tio) == -1) {
LOGERR;
return -1;
}
if (otio)
*otio = tio;
tio.c_iflag |= IGNPAR;
tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
#ifdef IUCLC
tio.c_iflag &= ~IUCLC;
#endif
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
#ifdef IEXTEN
tio.c_lflag &= ~IEXTEN;
#endif
tio.c_oflag &= ~OPOST;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSADRAIN, &tio) == -1) {
LOGERR;
return -1;
}
return 0;
}
/*
* ioRestoreMode() - Restore termios to tty fd
*
* @fd = tty fd
* @tio = termios structure for restore
* return: -1 error or 0 ok
*/
int
ioRestoreMode(int fd, struct termios tio)
{
if (tcsetattr(fd, TCSADRAIN, &tio) == -1) {
LOGERR;
return -1;
}
return 0;
}
/*
* ioForkPTY() - Fork new process with session leader and new TTY
*
* @ptyfd = master fd, pty
* @name = tty device name if not null
* @namesiz = name length, must be above 63 bytes.
* @term = termios for terminal
* @winz = winsize for terminal
* @otio = old termios structure for restore
* return: -1 error, 0 child process or >0 parent: pid of child
*/
pid_t
ioForkPTY(int *ptyfd, char * __restrict name, int namesiz, struct termios * __restrict term,
struct winsize * __restrict winz, struct termios * __restrict otio)
{
int ttyfd;
pid_t pid;
if (ioAllocPTY(ptyfd, &ttyfd, name, namesiz, term, winz))
return -1;
switch ((pid = fork())) {
case -1:
LOGERR;
return -1;
case 0:
if (ioSetOwnerTTY(name, getuid(), getgid()) == -1) {
ioFreePTY(*ptyfd, name);
return -1;
}
if (ioSetSidTTY(&ttyfd, name) == -1) {
ioFreePTY(*ptyfd, name);
return -1;
}
close(*ptyfd);
/* CHILD */
break;
default:
close(ttyfd);
/* PARENT */
break;
}
return pid;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>