Print this page
14249 pseudo-terminal nomenclature should reflect POSIX
Change-Id: Ib4a3cef899ff4c71b09cb0dc6878863c5e8357bc

*** 22,33 **** /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - #pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> --- 22,31 ----
*** 67,94 **** * prefix. */ #define PTY_CNTRL "pty" /* ! * Pseudo-terminal slave device file names start with the following * prefix. */ ! #define PTY_SLAVE "tty" /* ! * Specify the maximum suffix length for the control and slave device * names. */ #define PTY_MAX_SUFFIX 10 /* ! * Set the maximum length of the master and slave terminal device filenames, ! * including space for a terminating '\0'. */ #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ ! (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ ! sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ + PTY_MAX_SUFFIX + 1) /* * Set the maximum length of an input line. */ #define PTY_MAX_LINE 4096 --- 65,92 ---- * prefix. */ #define PTY_CNTRL "pty" /* ! * Pseudo-terminal subsidiary device file names start with the following * prefix. */ ! #define PTY_SUBSID "tty" /* ! * Specify the maximum suffix length for the control and subsidiary device * names. */ #define PTY_MAX_SUFFIX 10 /* ! * Set the maximum length of the manager and subsidiary terminal device ! * filenames, including space for a terminating '\0'. */ #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ ! (sizeof(PTY_SUBSID) > sizeof(PTY_CNTRL) ? \ ! sizeof(PTY_SUBSID) : sizeof(PTY_CNTRL))-1 \ + PTY_MAX_SUFFIX + 1) /* * Set the maximum length of an input line. */ #define PTY_MAX_LINE 4096
*** 108,126 **** * Set the timeout delay used to check for quickly arriving * sequential output from the application. */ #define PTY_READ_TIMEOUT 100000 /* micro-seconds */ ! static int pty_open_master(const char *prog, int *cntrl, char *slave_name); ! static int pty_open_slave(const char *prog, char *slave_name); ! static int pty_child(const char *prog, int slave, char *argv[]); static int pty_parent(const char *prog, int cntrl); static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); static GL_FD_EVENT_FN(pty_read_from_program); static int pty_write_to_fd(int fd, const char *string, int n); static void pty_child_exited(int sig); ! static int pty_master_readable(int fd, long usec); /*....................................................................... * Run a program with enhanced terminal editing facilities. * * Usage: --- 106,124 ---- * Set the timeout delay used to check for quickly arriving * sequential output from the application. */ #define PTY_READ_TIMEOUT 100000 /* micro-seconds */ ! static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name); ! static int pty_open_subsid(const char *prog, char *subsid_name); ! static int pty_child(const char *prog, int subsid, char *argv[]); static int pty_parent(const char *prog, int cntrl); static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); static GL_FD_EVENT_FN(pty_read_from_program); static int pty_write_to_fd(int fd, const char *string, int n); static void pty_child_exited(int sig); ! static int pty_manager_readable(int fd, long usec); /*....................................................................... * Run a program with enhanced terminal editing facilities. * * Usage:
*** 127,141 **** * enhance program [args...] */ int main(int argc, char *argv[]) { int cntrl = -1; /* The fd of the pseudo-terminal controller device */ ! int slave = -1; /* The fd of the pseudo-terminal slave device */ pid_t pid; /* The return value of fork() */ int status; /* The return statuses of the parent and child functions */ ! char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ ! /* pseudo-terminal. */ char *prog; /* The name of the program (ie. argv[0]) */ /* * Check the arguments. */ if(argc < 2) { --- 125,139 ---- * enhance program [args...] */ int main(int argc, char *argv[]) { int cntrl = -1; /* The fd of the pseudo-terminal controller device */ ! int subsid = -1; /* The fd of the pseudo-terminal subsidiary device */ pid_t pid; /* The return value of fork() */ int status; /* The return statuses of the parent and child functions */ ! char subsid_name[PTY_MAX_NAME]; /* The filename of the subsidiary end of */ ! /* the pseudo-terminal. */ char *prog; /* The name of the program (ie. argv[0]) */ /* * Check the arguments. */ if(argc < 2) {
*** 163,177 **** fflush(stderr); _exit(1); }; }; /* ! * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the ! * slave end of the pseudo-terminal. */ ! if(pty_open_master(prog, &cntrl, slave_name)) return 1; /* * Set up a signal handler to watch for the child process exiting. */ signal(SIGCHLD, pty_child_exited); --- 161,175 ---- fflush(stderr); _exit(1); }; }; /* ! * Open the manager side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the ! * subsidiary end of the pseudo-terminal. */ ! if(pty_open_manager(prog, &cntrl, subsid_name)) return 1; /* * Set up a signal handler to watch for the child process exiting. */ signal(SIGCHLD, pty_child_exited);
*** 199,277 **** */ if(pid!=0) { status = pty_parent(prog, cntrl); close(cntrl); } else { ! close(cntrl); /* The child doesn't use the slave device */ signal(SIGCHLD, pty_child_exited); ! if((slave = pty_open_slave(prog, slave_name)) >= 0) { ! status = pty_child(prog, slave, argv + 1); ! close(slave); } else { status = 1; }; }; return status; } /*....................................................................... ! * Open the master side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the ! * slave end of the pseudo-terminal. * * Input/Output: * prog const char * The name of this program. * cntrl int * The file descriptor of the pseudo-terminal * controller device will be assigned tp *cntrl. ! * slave_name char * The file-name of the pseudo-terminal slave device ! * will be recorded in slave_name[], which must have * at least PTY_MAX_NAME elements. * Output: * return int 0 - OK. * 1 - Error. */ ! static int pty_open_master(const char *prog, int *cntrl, char *slave_name) { ! char master_name[PTY_MAX_NAME]; /* The filename of the master device */ DIR *dir; /* The directory iterator */ struct dirent *file; /* A file in "/dev" */ /* * Mark the controller device as not opened yet. */ *cntrl = -1; /* * On systems with the Sys-V pseudo-terminal interface, we don't ! * have to search for a free master terminal. We just open /dev/ptmx, ! * and if there is a free master terminal device, we are given a file * descriptor connected to it. */ #if HAVE_SYSV_PTY *cntrl = open("/dev/ptmx", O_RDWR); if(*cntrl >= 0) { /* ! * Get the filename of the slave side of the pseudo-terminal. */ char *name = ptsname(*cntrl); if(name) { if(strlen(name)+1 > PTY_MAX_NAME) { ! fprintf(stderr, "%s: Slave pty filename too long.\n", prog); return 1; }; ! strlcpy(slave_name, name, PTY_MAX_NAME); /* ! * If unable to get the slave name, discard the controller file descriptor, ! * ready to try a search instead. */ } else { close(*cntrl); *cntrl = -1; }; } else { #endif /* * On systems without /dev/ptmx, or if opening /dev/ptmx failed, ! * we open one master terminal after another, until one that isn't * in use by another program is found. * * Open the devices directory. */ dir = opendir(PTY_DEV_DIR); --- 197,275 ---- */ if(pid!=0) { status = pty_parent(prog, cntrl); close(cntrl); } else { ! close(cntrl); /* The child doesn't use the subsidiary device */ signal(SIGCHLD, pty_child_exited); ! if((subsid = pty_open_subsid(prog, subsid_name)) >= 0) { ! status = pty_child(prog, subsid, argv + 1); ! close(subsid); } else { status = 1; }; }; return status; } /*....................................................................... ! * Open the manager side of a pseudo-terminal pair, and return * the corresponding file descriptor and the filename of the ! * subsidiary end of the pseudo-terminal. * * Input/Output: * prog const char * The name of this program. * cntrl int * The file descriptor of the pseudo-terminal * controller device will be assigned tp *cntrl. ! * subsid_name char * The file-name of the pseudo-terminal subsidiary device ! * will be recorded in subsid_name[], which must have * at least PTY_MAX_NAME elements. * Output: * return int 0 - OK. * 1 - Error. */ ! static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name) { ! char manager_name[PTY_MAX_NAME]; /* The filename of the manager device */ DIR *dir; /* The directory iterator */ struct dirent *file; /* A file in "/dev" */ /* * Mark the controller device as not opened yet. */ *cntrl = -1; /* * On systems with the Sys-V pseudo-terminal interface, we don't ! * have to search for a free manager terminal. We just open /dev/ptmx, ! * and if there is a free manager terminal device, we are given a file * descriptor connected to it. */ #if HAVE_SYSV_PTY *cntrl = open("/dev/ptmx", O_RDWR); if(*cntrl >= 0) { /* ! * Get the filename of the subsidiary side of the pseudo-terminal. */ char *name = ptsname(*cntrl); if(name) { if(strlen(name)+1 > PTY_MAX_NAME) { ! fprintf(stderr, "%s: Subsidiary pty filename too long.\n", prog); return 1; }; ! strlcpy(subsid_name, name, PTY_MAX_NAME); /* ! * If unable to get the subsidiary name, discard the controller file ! * descriptor, ready to try a search instead. */ } else { close(*cntrl); *cntrl = -1; }; } else { #endif /* * On systems without /dev/ptmx, or if opening /dev/ptmx failed, ! * we open one manager terminal after another, until one that isn't * in use by another program is found. * * Open the devices directory. */ dir = opendir(PTY_DEV_DIR);
*** 285,314 **** * directory. */ while(*cntrl < 0 && (file = readdir(dir))) { if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { /* ! * Get the common extension of the control and slave filenames. */ const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; if(strlen(ext) > PTY_MAX_SUFFIX) continue; /* * Attempt to open the control file. */ ! strlcpy(master_name, PTY_DEV_DIR, sizeof(master_name)); ! strlcat(master_name, PTY_CNTRL, sizeof(master_name)); ! strlcat(master_name, ext, sizeof(master_name)); ! *cntrl = open(master_name, O_RDWR); if(*cntrl < 0) continue; /* ! * Attempt to open the matching slave file. */ ! strlcpy(slave_name, PTY_DEV_DIR, PTY_MAX_NAME); ! strlcat(slave_name, PTY_SLAVE, PTY_MAX_NAME); ! strlcat(slave_name, ext, PTY_MAX_NAME); }; }; closedir(dir); #if HAVE_SYSV_PTY }; --- 283,312 ---- * directory. */ while(*cntrl < 0 && (file = readdir(dir))) { if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { /* ! * Get the common extension of the control and subsidiary filenames. */ const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; if(strlen(ext) > PTY_MAX_SUFFIX) continue; /* * Attempt to open the control file. */ ! strlcpy(manager_name, PTY_DEV_DIR, sizeof(manager_name)); ! strlcat(manager_name, PTY_CNTRL, sizeof(manager_name)); ! strlcat(manager_name, ext, sizeof(manager_name)); ! *cntrl = open(manager_name, O_RDWR); if(*cntrl < 0) continue; /* ! * Attempt to open the matching subsidiary file. */ ! strlcpy(subsid_name, PTY_DEV_DIR, PTY_MAX_NAME); ! strlcat(subsid_name, PTY_SUBSID, PTY_MAX_NAME); ! strlcat(subsid_name, ext, PTY_MAX_NAME); }; }; closedir(dir); #if HAVE_SYSV_PTY };
*** 319,330 **** if(*cntrl < 0) { fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); return 1; }; /* ! * System V systems require the program that opens the master to ! * grant access to the slave side of the pseudo-terminal. */ #ifdef HAVE_SYSV_PTY if(grantpt(*cntrl) < 0 || unlockpt(*cntrl) < 0) { fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, --- 317,328 ---- if(*cntrl < 0) { fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); return 1; }; /* ! * System V systems require the program that opens the manager to ! * grant access to the subsidiary side of the pseudo-terminal. */ #ifdef HAVE_SYSV_PTY if(grantpt(*cntrl) < 0 || unlockpt(*cntrl) < 0) { fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
*** 337,358 **** */ return 0; } /*....................................................................... ! * Open the slave end of a pseudo-terminal. * * Input: * prog const char * The name of this program. ! * slave_name char * The filename of the slave device. * Output: * return int The file descriptor of the successfully opened ! * slave device, or < 0 on error. */ ! static int pty_open_slave(const char *prog, char *slave_name) { ! int fd; /* The file descriptor of the slave device */ /* * Place the process in its own process group. In system-V based * OS's, this ensures that when the pseudo-terminal is opened, it * becomes the controlling terminal of the process. */ --- 335,356 ---- */ return 0; } /*....................................................................... ! * Open the subsidiary end of a pseudo-terminal. * * Input: * prog const char * The name of this program. ! * subsid_name char * The filename of the subsidiary device. * Output: * return int The file descriptor of the successfully opened ! * subsidiary device, or < 0 on error. */ ! static int pty_open_subsid(const char *prog, char *subsid_name) { ! int fd; /* The file descriptor of the subsidiary device */ /* * Place the process in its own process group. In system-V based * OS's, this ensures that when the pseudo-terminal is opened, it * becomes the controlling terminal of the process. */
*** 362,381 **** return -1; }; /* * Attempt to open the specified device. */ ! fd = open(slave_name, O_RDWR); if(fd < 0) { ! fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", prog, strerror(errno)); return -1; }; /* * On system-V streams based systems, we need to push the stream modules * that implement pseudo-terminal and termio interfaces. At least on ! * Solaris, which pushes these automatically when a slave is opened, * this is redundant, so ignore errors when pushing the modules. */ #if HAVE_SYSV_PTY (void) ioctl(fd, I_PUSH, "ptem"); (void) ioctl(fd, I_PUSH, "ldterm"); --- 360,379 ---- return -1; }; /* * Attempt to open the specified device. */ ! fd = open(subsid_name, O_RDWR); if(fd < 0) { ! fprintf(stderr, "%s: Unable to open pty subsidiary device (%s).\n", prog, strerror(errno)); return -1; }; /* * On system-V streams based systems, we need to push the stream modules * that implement pseudo-terminal and termio interfaces. At least on ! * Solaris, which pushes these automatically when a subsidiary is opened, * this is redundant, so ignore errors when pushing the modules. */ #if HAVE_SYSV_PTY (void) ioctl(fd, I_PUSH, "ptem"); (void) ioctl(fd, I_PUSH, "ldterm");
*** 496,510 **** return waserr ? 1 : status; } /*....................................................................... * Run the user's program, with its stdin and stdout connected to the ! * slave end of the psuedo-terminal. * * Input: * prog const char * The name of this program. ! * slave int The file descriptor of the slave end of the * pseudo terminal. * argv char *[] The argument vector to pass to the user's program, * where argv[0] is the name of the user's program, * and the last argument is followed by a pointer * to NULL. --- 494,508 ---- return waserr ? 1 : status; } /*....................................................................... * Run the user's program, with its stdin and stdout connected to the ! * subsidiary end of the psuedo-terminal. * * Input: * prog const char * The name of this program. ! * subsid int The file descriptor of the subsidiary end of the * pseudo terminal. * argv char *[] The argument vector to pass to the user's program, * where argv[0] is the name of the user's program, * and the last argument is followed by a pointer * to NULL.
*** 512,548 **** * return int If this function returns at all, an error must * have occured when trying to overlay the process * with the user's program. In this case 1 is * returned. */ ! static int pty_child(const char *prog, int slave, char *argv[]) { struct termios attr; /* The terminal attributes */ /* * We need to stop the pseudo-terminal from echoing everything that we send it. */ ! if(tcgetattr(slave, &attr)) { fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, strerror(errno)); return 1; }; attr.c_lflag &= ~(ECHO); ! while(tcsetattr(slave, TCSADRAIN, &attr)) { if(errno != EINTR) { fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); return 1; }; }; /* ! * Arrange for stdin, stdout and stderr to be connected to the slave device, ! * ignoring errors that imply that either stdin or stdout is closed. */ ! while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR) ; ! while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR) ; ! while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR) ; /* * Run the user's program. */ if(execvp(argv[0], argv) < 0) { --- 510,546 ---- * return int If this function returns at all, an error must * have occured when trying to overlay the process * with the user's program. In this case 1 is * returned. */ ! static int pty_child(const char *prog, int subsid, char *argv[]) { struct termios attr; /* The terminal attributes */ /* * We need to stop the pseudo-terminal from echoing everything that we send it. */ ! if(tcgetattr(subsid, &attr)) { fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, strerror(errno)); return 1; }; attr.c_lflag &= ~(ECHO); ! while(tcsetattr(subsid, TCSADRAIN, &attr)) { if(errno != EINTR) { fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); return 1; }; }; /* ! * Arrange for stdin, stdout and stderr to be connected to the subsidiary ! * device, ignoring errors that imply that either stdin or stdout is closed. */ ! while(dup2(subsid, STDIN_FILENO) < 0 && errno==EINTR) ; ! while(dup2(subsid, STDOUT_FILENO) < 0 && errno==EINTR) ; ! while(dup2(subsid, STDERR_FILENO) < 0 && errno==EINTR) ; /* * Run the user's program. */ if(execvp(argv[0], argv) < 0) {
*** 646,656 **** * Move any unwritten text following the newline, to the start of the * buffer. */ memmove(rbuff, nextp, len - (nextp - rbuff) + 1); }; ! } while(pty_master_readable(fd, PTY_READ_TIMEOUT)); /* * Make the incomplete line in the output buffer the current prompt. */ gl_replace_prompt(gl, rbuff); return GLFD_REFRESH; --- 644,654 ---- * Move any unwritten text following the newline, to the start of the * buffer. */ memmove(rbuff, nextp, len - (nextp - rbuff) + 1); }; ! } while(pty_manager_readable(fd, PTY_READ_TIMEOUT)); /* * Make the incomplete line in the output buffer the current prompt. */ gl_replace_prompt(gl, rbuff); return GLFD_REFRESH;
*** 684,694 **** } /*....................................................................... * This is the signal handler that is called when the child process * that is running the user's program exits for any reason. It closes ! * the slave end of the terminal, so that gl_get_line() in the parent * process sees an end of file. */ static void pty_child_exited(int sig) { raise(SIGINT); --- 682,692 ---- } /*....................................................................... * This is the signal handler that is called when the child process * that is running the user's program exits for any reason. It closes ! * the subsidiary end of the terminal, so that gl_get_line() in the parent * process sees an end of file. */ static void pty_child_exited(int sig) { raise(SIGINT);
*** 705,715 **** * Output: * return int 0 - No data is waiting to be read (or select isn't * available). * 1 - Data is waiting to be read. */ ! static int pty_master_readable(int fd, long usec) { #if HAVE_SELECT fd_set rfds; /* The set of file descriptors to check */ struct timeval timeout; /* The timeout */ FD_ZERO(&rfds); --- 703,713 ---- * Output: * return int 0 - No data is waiting to be read (or select isn't * available). * 1 - Data is waiting to be read. */ ! static int pty_manager_readable(int fd, long usec) { #if HAVE_SELECT fd_set rfds; /* The set of file descriptors to check */ struct timeval timeout; /* The timeout */ FD_ZERO(&rfds);