/* _XOPEN_SOURCE unlocks pty/ptmx/pts declarations. */ #define _XOPEN_SOURCE 600 /* _GNU_SOURCE unlocks the ptsname_r declaration*/ #define _GNU_SOURCE #include #include #include #include /* TIOC* constants */ #include #ifdef PATH_MAX # define SNAME_MAX PATH_MAX #else # define SNAME_MAX 512 #endif /* PATH_MAX */ /* Allocate PTY master and slave file descriptors. * errno will have been set if newpty() fails. * * NOTE: This function is my alternative to GLibC's * openpty() function. It exists as a learning resource. * REF: glibc.git:/login/openpty.c */ int mkpty(int *fdmx, int *fds) { int _fdmx = -1, _fds = -1; char sname[SNAME_MAX]; // Configure PTY master (file descriptor) _fdmx = posix_openpt(O_RDWR | O_NOCTTY); if (_fdmx == -1) return EXIT_FAILURE; if (grantpt(_fdmx)) goto fail; if (unlockpt(_fdmx)) goto fail; #ifdef TIOCGPTPEER /* Try to allocate slave fd solely based on PTMX fd first. */ _fds = ioctl(_fdmx, TIOCGPTPEER, O_RDWR | O_NOCTTY); #endif if (_fds == -1) { /* Fallback to path-based slave fd allocation * (if the kernel doesn't support TIOCGPTPEER, ie Linux <4.13) */ if(ptsname_r(_fdmx, sname, sizeof(sname))) goto fail; _fds = open(sname, O_RDWR | O_NOCTTY); if (_fds == -1) goto fail; } // Propagate file descriptors via parameters *fdmx = _fdmx; *fds = _fds; return EXIT_SUCCESS; fail: if (_fdmx == -1) { close(_fdmx); if (_fds == -1) close(_fds); } return EXIT_FAILURE; } /* */ pid_t forkmkpty() { int fdmx, fds; pid_t pid; if (mkpty(&fdmx, &fds)) return EXIT_FAILURE; switch (pid = fork()) { case -1: close(fdmx); close(fds); return -1; case 0: /* Child Process */ close(fdmx); break; default: /* Parent Process */ close(fds); break; } /* Both Processes */ return pid; } /* Set pseudoterminal slave's window size. * Returns 0 on success, and fails with -1 if the kernel doesn't * implement this, or 1 for general errors (errno will be set). * NOTE: Typically this is part of a glibc openpty() call. */ int setptsxy(const unsigned short rows, const unsigned short cols, const int fds) { #ifndef TIOCSWINSZ /* Fail if kernel doesn't support TIOCSWINSZ. */ return -1; #else struct winsize win = { .ws_row = rows, .ws_col = cols, }; if (ioctl(fds, TIOCSWINSZ, &win)) return EXIT_FAILURE; return EXIT_SUCCESS; #endif /* TIOCSWINSZ */ } /* Get pseudoterminal slave's window size. * Returns 0 on success, and fails with -1 if the kernel doesn't * implement this, or 1 for general errors (errno will be set). */ int getptsxy(unsigned short *rows, unsigned short *cols, const int fds) { #ifndef TIOCGWINSZ /* Fail if kernel doesn't support TIOCGWINSZ. */ return -1; #else struct winsize win = (struct winsize){ 0 }; if (ioctl(fds, TIOCGWINSZ, &win)) return EXIT_FAILURE; *rows = win.ws_row; *cols = win.ws_col; return EXIT_SUCCESS; #endif /* TIOCGWINSZ */ }