/************************************************************************* * (C) 2011 AITNET ltd - Sofia/Bulgaria - * by Michael Pounov * * $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 Copyright 2004 - 2016 by Michael Pounov . 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 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; }