/* This file is part of Zutty. * Copyright (C) 2020 Tom Szilagyi * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * See the file LICENSE for the full license. */ /* The source code in this file is inspired by code samples in the book * Advanced Programming in the UNIX Environment, 3rd Edition * by W. Richard Stevens & Stephen A. Rago * Addison-Wesley, 2013 * * The original example code of the book is available from * http://www.apuebook.com/code3e.html */ #include "log.h" #include "pty.h" #define _POSIX_C_SOURCE 200809L #if defined(SOLARIS) /* Solaris 10 */ #define _XOPEN_SOURCE 600 #else #define _XOPEN_SOURCE 700 #endif #include #include #include #include #include #include #include #include #include #if defined(BSD) || defined(MACOS) || !defined(TIOCGWINSZ) #include #endif #if defined(SOLARIS) #include #endif namespace zutty { int ptym_open (char* pts_name, int pts_namesz) { char* ptr; int fdm; if ((fdm = posix_openpt (O_RDWR)) < 0) SYS_ERROR ("can't open master pty: posix_openpt()"); if (grantpt (fdm) < 0) // grant access to slave SYS_ERROR ("can't open master pty: grantpt()"); if (unlockpt (fdm) < 0) // clear slave's lock flag SYS_ERROR ("can't open master pty: unlockpt()"); if ((ptr = ptsname (fdm)) == nullptr) // get slave's name SYS_ERROR ("can't open master pty: ptsname()"); // Return slave name, null-terminated to handle strlen(ptr) > pts_namesz. strncpy (pts_name, ptr, pts_namesz); pts_name [pts_namesz - 1] = '\0'; return fdm; } int ptys_open (char* pts_name) { int fds = open (pts_name, O_RDWR); if (fds < 0) SYS_ERROR ("can't open slave pty: open()"); #if defined(SOLARIS) // Check if stream is already set up by autopush facility. int setup; if ((setup = ioctl (fds, I_FIND, "ldterm")) < 0) SYS_ERROR ("can't open slave pty: ioctl(I_FIND, ldterm)"); if (setup == 0) { if (ioctl (fds, I_PUSH, "ptem") < 0) SYS_ERROR ("can't open slave pty: ioctl(I_PUSH, ptem)"); if (ioctl (fds, I_PUSH, "ldterm") < 0) SYS_ERROR ("can't open slave pty: ioctl(I_PUSH, ldterm)"); if (ioctl (fds, I_PUSH, "ttcompat") < 0) SYS_ERROR ("can't open slave pty: ioctl(I_PUSH, ttcompat)"); } #endif return fds; } pid_t pty_fork (int& o_ptyFd, int cols, int rows) { pid_t pid; char pts_name [20]; int fdm = ptym_open (pts_name, sizeof (pts_name)); pid = fork (); if (pid < 0) { return pid; } else if (pid == 0) // child process { if (setsid () < 0) SYS_ERROR ("setsid"); // System V acquires controlling terminal on open (). int fds = ptys_open (pts_name); close (fdm); // all done with master in child #if defined(BSD) // TIOCSCTTY is the BSD way to acquire a controlling terminal. if (ioctl (fds, TIOCSCTTY, nullptr) < 0) SYS_ERROR ("TIOCSCTTY"); #endif // Set the pty slave's window size. pty_resize (fds, cols, rows); // Slave becomes stdin/stdout/stderr of child. redirectFds (fds); #if defined(LINUX) || defined(MACOS) // Setup terminal attributes struct termios term; if (tcgetattr (STDIN_FILENO, &term) < 0) SYS_ERROR ("tcgetattr"); term.c_iflag |= IUTF8; if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0) SYS_ERROR ("tcsetattr"); #endif } else // parent process { o_ptyFd = fdm; } return pid; } void pty_resize (int ptyFd, int cols, int rows) { struct winsize wsize {}; wsize.ws_col = cols; wsize.ws_row = rows; if (ioctl (ptyFd, TIOCSWINSZ, &wsize) < 0) SYS_ERROR("TIOCSWINSZ on pty"); } } // namespace zutty