--- libaitio/src/pty.c 2011/09/19 21:59:58 1.1.2.1 +++ libaitio/src/pty.c 2012/03/27 21:37:56 1.2.8.1 @@ -3,7 +3,7 @@ * by Michael Pounov * * $Author: misho $ -* $Id: pty.c,v 1.1.2.1 2011/09/19 21:59:58 misho Exp $ +* $Id: pty.c,v 1.2.8.1 2012/03/27 21:37:56 misho Exp $ * ************************************************************************** The ELWIX and AITNET software is distributed under the following @@ -12,7 +12,7 @@ terms: All of the documentation and software included in the ELWIX and AITNET Releases is copyrighted by ELWIX - Sofia/Bulgaria -Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 +Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 by Michael Pounov . All rights reserved. Redistribution and use in source and binary forms, with or without @@ -47,7 +47,8 @@ SUCH DAMAGE. /* - * ioAllocPTY() Allocate new PTY + * ioAllocPTY() Allocate new PTY and TTY + * * @ptyfd = master fd, pty * @ttyfd = slave fd, tty * @name = tty device name if not null @@ -76,17 +77,269 @@ ioAllocPTY(int *ptyfd, int *ttyfd, char * __restrict n } /* - * ioFreePTY() Release PTY + * ioFreePTY() Release PTY and TTY device + * + * @ptyfd = master fd, pty (==-1 skip closing pty) * @ttyname = tty filename * return: none */ inline void -ioFreePTY(const char *ttyname) +ioFreePTY(int ptyfd, const char *ttyname) { assert(ttyname); if (!ttyname) return; - chown(ttyname, (uid_t) 0, (gid_t) 0); - chmod(ttyname, (mode_t) 0666); + 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 + */ +inline 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, "Error:: 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, "Error:: 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 + */ +inline 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 + */ +inline 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; }