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


   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #include <errno.h>
  33 #include <signal.h>
  34 #include <locale.h>
  35 
  36 #include <unistd.h>
  37 #include <termios.h>
  38 
  39 #ifdef HAVE_SELECT
  40 #ifdef HAVE_SYS_SELECT_H
  41 #include <sys/select.h>
  42 #endif
  43 #endif
  44 
  45 #include <fcntl.h>
  46 #include <sys/time.h>
  47 #include <sys/types.h>
  48 #include <sys/wait.h>


  52 #include <stropts.h>    /* System-V stream I/O */
  53 char *ptsname(int fd);
  54 int grantpt(int fd);
  55 int unlockpt(int fd);
  56 #endif
  57 
  58 #include "libtecla.h"
  59 
  60 /*
  61  * Pseudo-terminal devices are found in the following directory.
  62  */
  63 #define PTY_DEV_DIR "/dev/"
  64 
  65 /*
  66  * Pseudo-terminal controller device file names start with the following
  67  * prefix.
  68  */
  69 #define PTY_CNTRL "pty"
  70 
  71 /*
  72  * Pseudo-terminal slave device file names start with the following
  73  * prefix.
  74  */
  75 #define PTY_SLAVE "tty"
  76 
  77 /*
  78  * Specify the maximum suffix length for the control and slave device
  79  * names.
  80  */
  81 #define PTY_MAX_SUFFIX 10
  82 
  83 /*
  84  * Set the maximum length of the master and slave terminal device filenames,
  85  * including space for a terminating '\0'.
  86  */
  87 #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \
  88                       (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \
  89                        sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \
  90                       + PTY_MAX_SUFFIX + 1)
  91 /*
  92  * Set the maximum length of an input line.
  93  */
  94 #define PTY_MAX_LINE 4096
  95 
  96 /*
  97  * Set the size of the buffer used for accumulating bytes written by the
  98  * user's terminal to its stdout.
  99  */
 100 #define PTY_MAX_READ 1000
 101 
 102 /*
 103  * Set the amount of memory used to record history.
 104  */
 105 #define PTY_HIST_SIZE 10000
 106 
 107 /*
 108  * Set the timeout delay used to check for quickly arriving
 109  * sequential output from the application.
 110  */
 111 #define PTY_READ_TIMEOUT 100000    /* micro-seconds */
 112 
 113 static int pty_open_master(const char *prog, int *cntrl, char *slave_name);
 114 static int pty_open_slave(const char *prog, char *slave_name);
 115 static int pty_child(const char *prog, int slave, char *argv[]);
 116 static int pty_parent(const char *prog, int cntrl);
 117 static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);
 118 static GL_FD_EVENT_FN(pty_read_from_program);
 119 static int pty_write_to_fd(int fd, const char *string, int n);
 120 static void pty_child_exited(int sig);
 121 static int pty_master_readable(int fd, long usec);
 122 
 123 /*.......................................................................
 124  * Run a program with enhanced terminal editing facilities.
 125  *
 126  * Usage:
 127  *  enhance program [args...]
 128  */
 129 int main(int argc, char *argv[])
 130 {
 131   int cntrl = -1;  /* The fd of the pseudo-terminal controller device */
 132   int slave = -1;  /* The fd of the pseudo-terminal slave device */
 133   pid_t pid;       /* The return value of fork() */
 134   int status;      /* The return statuses of the parent and child functions */
 135   char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */
 136                                  /*  pseudo-terminal. */
 137   char *prog;      /* The name of the program (ie. argv[0]) */
 138 /*
 139  * Check the arguments.
 140  */
 141   if(argc < 2) {
 142     fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
 143     return 1;
 144   };
 145 /*
 146  * Get the name of the program.
 147  */
 148   prog = argv[0];
 149 /*
 150  * If the user has the LC_CTYPE or LC_ALL environment variables set,
 151  * enable display of characters corresponding to the specified locale.
 152  */
 153   (void) setlocale(LC_CTYPE, "");
 154 /*
 155  * If the program is taking its input from a pipe or a file, or
 156  * sending its output to something other than a terminal, run the
 157  * program without tecla.
 158  */
 159   if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
 160     if(execvp(argv[1], argv + 1) < 0) {
 161       fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
 162               strerror(errno));
 163       fflush(stderr);
 164       _exit(1);
 165     };
 166   };
 167 /*
 168  * Open the master side of a pseudo-terminal pair, and return
 169  * the corresponding file descriptor and the filename of the
 170  * slave end of the pseudo-terminal.
 171  */
 172   if(pty_open_master(prog, &cntrl, slave_name))
 173     return 1;
 174 /*
 175  * Set up a signal handler to watch for the child process exiting.
 176  */
 177   signal(SIGCHLD, pty_child_exited);
 178 /*
 179  * The above signal handler sends the parent process a SIGINT signal.
 180  * This signal is caught by gl_get_line(), which resets the terminal
 181  * settings, and if the application signal handler for this signal
 182  * doesn't abort the process, gl_get_line() returns NULL with errno
 183  * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
 184  * returns and we have a chance to cleanup.
 185  */
 186   signal(SIGINT, SIG_IGN);
 187 /*
 188  * We will read user input in one process, and run the user's program
 189  * in a child process.
 190  */
 191   pid = fork();
 192   if(pid < 0) {
 193     fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
 194             strerror(errno));
 195     return 1;
 196   };
 197 /*
 198  * Are we the parent?
 199  */
 200   if(pid!=0) {
 201     status = pty_parent(prog, cntrl);
 202     close(cntrl);
 203   } else {
 204     close(cntrl); /* The child doesn't use the slave device */
 205     signal(SIGCHLD, pty_child_exited);
 206     if((slave = pty_open_slave(prog, slave_name)) >= 0) {
 207       status = pty_child(prog, slave, argv + 1);
 208       close(slave);
 209     } else {
 210       status = 1;
 211     };
 212   };
 213   return status;
 214 }
 215 
 216 /*.......................................................................
 217  * Open the master side of a pseudo-terminal pair, and return
 218  * the corresponding file descriptor and the filename of the
 219  * slave end of the pseudo-terminal.
 220  *
 221  * Input/Output:
 222  *  prog  const char *  The name of this program.
 223  *  cntrl        int *  The file descriptor of the pseudo-terminal
 224  *                      controller device will be assigned tp *cntrl.
 225  *  slave_name  char *  The file-name of the pseudo-terminal slave device
 226  *                      will be recorded in slave_name[], which must have
 227  *                      at least PTY_MAX_NAME elements.
 228  * Output:
 229  *  return       int    0 - OK.
 230  *                      1 - Error.
 231  */
 232 static int pty_open_master(const char *prog, int *cntrl, char *slave_name)
 233 {
 234   char master_name[PTY_MAX_NAME]; /* The filename of the master device */
 235   DIR *dir;                       /* The directory iterator */
 236   struct dirent *file;            /* A file in "/dev" */
 237 /*
 238  * Mark the controller device as not opened yet.
 239  */
 240   *cntrl = -1;
 241 /*
 242  * On systems with the Sys-V pseudo-terminal interface, we don't
 243  * have to search for a free master terminal. We just open /dev/ptmx,
 244  * and if there is a free master terminal device, we are given a file
 245  * descriptor connected to it.
 246  */
 247 #if HAVE_SYSV_PTY
 248   *cntrl = open("/dev/ptmx", O_RDWR);
 249   if(*cntrl >= 0) {
 250 /*
 251  * Get the filename of the slave side of the pseudo-terminal.
 252  */
 253     char *name = ptsname(*cntrl);
 254     if(name) {
 255       if(strlen(name)+1 > PTY_MAX_NAME) {
 256         fprintf(stderr, "%s: Slave pty filename too long.\n", prog);
 257         return 1;
 258       };
 259       strlcpy(slave_name, name, PTY_MAX_NAME);
 260 /*
 261  * If unable to get the slave name, discard the controller file descriptor,
 262  * ready to try a search instead.
 263  */
 264     } else {
 265       close(*cntrl);
 266       *cntrl = -1;
 267     };
 268   } else {
 269 #endif
 270 /*
 271  * On systems without /dev/ptmx, or if opening /dev/ptmx failed,
 272  * we open one master terminal after another, until one that isn't
 273  * in use by another program is found.
 274  *
 275  * Open the devices directory.
 276  */
 277     dir = opendir(PTY_DEV_DIR);
 278     if(!dir) {
 279       fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR,
 280               strerror(errno));
 281       return 1;
 282     };
 283 /*
 284  * Look for pseudo-terminal controller device files in the devices
 285  * directory.
 286  */
 287     while(*cntrl < 0 && (file = readdir(dir))) {
 288       if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {
 289 /*
 290  * Get the common extension of the control and slave filenames.
 291  */
 292         const char *ext = file->d_name + sizeof(PTY_CNTRL)-1;
 293         if(strlen(ext) > PTY_MAX_SUFFIX)
 294           continue;
 295 /*
 296  * Attempt to open the control file.
 297  */
 298         strlcpy(master_name, PTY_DEV_DIR, sizeof(master_name));
 299         strlcat(master_name, PTY_CNTRL, sizeof(master_name));
 300         strlcat(master_name, ext, sizeof(master_name));
 301         *cntrl = open(master_name, O_RDWR);
 302         if(*cntrl < 0)
 303           continue;
 304 /*
 305  * Attempt to open the matching slave file.
 306  */
 307         strlcpy(slave_name, PTY_DEV_DIR, PTY_MAX_NAME);
 308         strlcat(slave_name, PTY_SLAVE, PTY_MAX_NAME);
 309         strlcat(slave_name, ext, PTY_MAX_NAME);
 310       };
 311     };
 312     closedir(dir);
 313 #if HAVE_SYSV_PTY
 314   };
 315 #endif
 316 /*
 317  * Did we fail to find a pseudo-terminal pair that we could open?
 318  */
 319   if(*cntrl < 0) {
 320     fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog);
 321     return 1;
 322   };
 323 /*
 324  * System V systems require the program that opens the master to
 325  * grant access to the slave side of the pseudo-terminal.
 326  */
 327 #ifdef HAVE_SYSV_PTY
 328   if(grantpt(*cntrl) < 0 ||
 329      unlockpt(*cntrl) < 0) {
 330     fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
 331             strerror(errno));
 332     return 1;
 333   };
 334 #endif
 335 /*
 336  * Success.
 337  */
 338   return 0;
 339 }
 340 
 341 /*.......................................................................
 342  * Open the slave end of a pseudo-terminal.
 343  *
 344  * Input:
 345  *  prog   const char *  The name of this program.
 346  *  slave_name   char *  The filename of the slave device.
 347  * Output:
 348  *  return        int    The file descriptor of the successfully opened
 349  *                       slave device, or < 0 on error.
 350  */
 351 static int pty_open_slave(const char *prog, char *slave_name)
 352 {
 353   int fd;  /* The file descriptor of the slave device */
 354 /*
 355  * Place the process in its own process group. In system-V based
 356  * OS's, this ensures that when the pseudo-terminal is opened, it
 357  * becomes the controlling terminal of the process.
 358  */
 359   if(setsid() < 0) {
 360     fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog,
 361             strerror(errno));
 362     return -1;
 363   };
 364 /*
 365  * Attempt to open the specified device.
 366  */
 367   fd = open(slave_name, O_RDWR);
 368   if(fd < 0) {
 369     fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n",
 370             prog, strerror(errno));
 371     return -1;
 372   };
 373 /*
 374  * On system-V streams based systems, we need to push the stream modules
 375  * that implement pseudo-terminal and termio interfaces. At least on
 376  * Solaris, which pushes these automatically when a slave is opened,
 377  * this is redundant, so ignore errors when pushing the modules.
 378  */
 379 #if HAVE_SYSV_PTY
 380   (void) ioctl(fd, I_PUSH, "ptem");
 381   (void) ioctl(fd, I_PUSH, "ldterm");
 382 /*
 383  * On BSD based systems other than SunOS 4.x, the following makes the
 384  * pseudo-terminal the controlling terminal of the child process.
 385  * According to the pseudo-terminal example code in Steven's
 386  * Advanced programming in the unix environment, the !defined(CIBAUD)
 387  * part of the clause prevents this from being used under SunOS. Since
 388  * I only have his code with me, and won't have access to the book,
 389  * I don't know why this is necessary.
 390  */
 391 #elif defined(TIOCSCTTY) && !defined(CIBAUD)
 392   if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) {
 393     fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n",
 394             prog, strerror(errno));
 395     close(fd);
 396     return -1;


 481  */
 482   gl = del_GetLine(gl);
 483 /*
 484  * Delete the read buffer.
 485  */
 486   if(rbuff)
 487     free(rbuff);
 488 /*
 489  * Wait for the user's program to end.
 490  */
 491   (void) wait(&status);
 492 /*
 493  * Return either our error status, or the return status of the child
 494  * program.
 495  */
 496   return waserr ? 1 : status;
 497 }
 498 
 499 /*.......................................................................
 500  * Run the user's program, with its stdin and stdout connected to the
 501  * slave end of the psuedo-terminal.
 502  *
 503  * Input:
 504  *  prog  const char *   The name of this program.
 505  *  slave        int     The file descriptor of the slave end of the
 506  *                       pseudo terminal.
 507  *  argv        char *[] The argument vector to pass to the user's program,
 508  *                       where argv[0] is the name of the user's program,
 509  *                       and the last argument is followed by a pointer
 510  *                       to NULL.
 511  * Output:
 512  *  return   int         If this function returns at all, an error must
 513  *                       have occured when trying to overlay the process
 514  *                       with the user's program. In this case 1 is
 515  *                       returned.
 516  */
 517 static int pty_child(const char *prog, int slave, char *argv[])
 518 {
 519   struct termios attr; /* The terminal attributes */
 520 /*
 521  * We need to stop the pseudo-terminal from echoing everything that we send it.
 522  */
 523   if(tcgetattr(slave, &attr)) {
 524     fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog,
 525             strerror(errno));
 526     return 1;
 527   };
 528   attr.c_lflag &= ~(ECHO);
 529   while(tcsetattr(slave, TCSADRAIN, &attr)) {
 530     if(errno != EINTR) {
 531       fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno));
 532       return 1;
 533     };
 534   };
 535 /*
 536  * Arrange for stdin, stdout and stderr to be connected to the slave device,
 537  * ignoring errors that imply that either stdin or stdout is closed.
 538  */
 539   while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR)
 540     ;
 541   while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR)
 542     ;
 543   while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR)
 544     ;
 545 /*
 546  * Run the user's program.
 547  */
 548   if(execvp(argv[0], argv) < 0) {
 549     fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0],
 550             strerror(errno));
 551     fflush(stderr);
 552     _exit(1);
 553   };
 554   return 0;  /* This should never be reached */
 555 }
 556 
 557 /*.......................................................................
 558  * This is the event-handler that is called by gl_get_line() whenever
 559  * there is tet waiting to be read from the user's program, via the
 560  * controller end of the pseudo-terminal. See libtecla.h for details
 561  * about its arguments.
 562  */
 563 static GL_FD_EVENT_FN(pty_read_from_program)


 631         (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1);
 632 /*
 633  * Write everything up to the last newline to stdout.
 634  */
 635       (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff);
 636 /*
 637  * Start a new line.
 638  */
 639       (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2);
 640 /*
 641  * Skip trailing carriage returns and newlines.
 642  */
 643       while(*nextp=='\n' || *nextp=='\r')
 644         nextp++;
 645 /*
 646  * Move any unwritten text following the newline, to the start of the
 647  * buffer.
 648  */
 649       memmove(rbuff, nextp, len - (nextp - rbuff) + 1);
 650     };
 651   } while(pty_master_readable(fd, PTY_READ_TIMEOUT));
 652 /*
 653  * Make the incomplete line in the output buffer the current prompt.
 654  */
 655   gl_replace_prompt(gl, rbuff);
 656   return GLFD_REFRESH;
 657 }
 658 
 659 /*.......................................................................
 660  * Write a given string to a specified file descriptor.
 661  *
 662  * Input:
 663  *  fd             int     The file descriptor to write to.
 664  *  string  const char *   The string to write (of at least 'n' characters).
 665  *  n              int     The number of characters to write.
 666  * Output:
 667  *  return         int     0 - OK.
 668  *                         1 - Error.
 669  */
 670 static int pty_write_to_fd(int fd, const char *string, int n)
 671 {
 672   int ndone = 0;  /* The number of characters written so far */
 673 /*
 674  * Do as many writes as are needed to write the whole string.
 675  */
 676   while(ndone < n) {
 677     int nnew = write(fd, string + ndone, n - ndone);
 678     if(nnew > 0)
 679       ndone += nnew;
 680     else if(errno != EINTR)
 681       return 1;
 682   };
 683   return 0;
 684 }
 685 
 686 /*.......................................................................
 687  * This is the signal handler that is called when the child process
 688  * that is running the user's program exits for any reason. It closes
 689  * the slave end of the terminal, so that gl_get_line() in the parent
 690  * process sees an end of file.
 691  */
 692 static void pty_child_exited(int sig)
 693 {
 694   raise(SIGINT);
 695 }
 696 
 697 /*.......................................................................
 698  * Return non-zero after a given amount of time if there is data waiting
 699  * to be read from a given file descriptor.
 700  *
 701  * Input:
 702  *  fd        int  The descriptor to watch.
 703  *  usec     long  The number of micro-seconds to wait for input to
 704  *                 arrive before giving up.
 705  * Output:
 706  *  return    int  0 - No data is waiting to be read (or select isn't
 707  *                     available).
 708  *                 1 - Data is waiting to be read.
 709  */
 710 static int pty_master_readable(int fd, long usec)
 711 {
 712 #if HAVE_SELECT
 713   fd_set rfds;             /* The set of file descriptors to check */
 714   struct timeval timeout;  /* The timeout */
 715   FD_ZERO(&rfds);
 716   FD_SET(fd, &rfds);
 717   timeout.tv_sec = 0;
 718   timeout.tv_usec = usec;
 719   return select(fd+1, &rfds, NULL, NULL, &timeout) == 1;
 720 #else
 721   return 0;
 722 #endif
 723 }


   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 


  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 #include <errno.h>
  31 #include <signal.h>
  32 #include <locale.h>
  33 
  34 #include <unistd.h>
  35 #include <termios.h>
  36 
  37 #ifdef HAVE_SELECT
  38 #ifdef HAVE_SYS_SELECT_H
  39 #include <sys/select.h>
  40 #endif
  41 #endif
  42 
  43 #include <fcntl.h>
  44 #include <sys/time.h>
  45 #include <sys/types.h>
  46 #include <sys/wait.h>


  50 #include <stropts.h>    /* System-V stream I/O */
  51 char *ptsname(int fd);
  52 int grantpt(int fd);
  53 int unlockpt(int fd);
  54 #endif
  55 
  56 #include "libtecla.h"
  57 
  58 /*
  59  * Pseudo-terminal devices are found in the following directory.
  60  */
  61 #define PTY_DEV_DIR "/dev/"
  62 
  63 /*
  64  * Pseudo-terminal controller device file names start with the following
  65  * prefix.
  66  */
  67 #define PTY_CNTRL "pty"
  68 
  69 /*
  70  * Pseudo-terminal subsidiary device file names start with the following
  71  * prefix.
  72  */
  73 #define PTY_SUBSID "tty"
  74 
  75 /*
  76  * Specify the maximum suffix length for the control and subsidiary device
  77  * names.
  78  */
  79 #define PTY_MAX_SUFFIX 10
  80 
  81 /*
  82  * Set the maximum length of the manager and subsidiary terminal device
  83  * filenames, including space for a terminating '\0'.
  84  */
  85 #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \
  86                       (sizeof(PTY_SUBSID) > sizeof(PTY_CNTRL) ? \
  87                        sizeof(PTY_SUBSID) : sizeof(PTY_CNTRL))-1 \
  88                       + PTY_MAX_SUFFIX + 1)
  89 /*
  90  * Set the maximum length of an input line.
  91  */
  92 #define PTY_MAX_LINE 4096
  93 
  94 /*
  95  * Set the size of the buffer used for accumulating bytes written by the
  96  * user's terminal to its stdout.
  97  */
  98 #define PTY_MAX_READ 1000
  99 
 100 /*
 101  * Set the amount of memory used to record history.
 102  */
 103 #define PTY_HIST_SIZE 10000
 104 
 105 /*
 106  * Set the timeout delay used to check for quickly arriving
 107  * sequential output from the application.
 108  */
 109 #define PTY_READ_TIMEOUT 100000    /* micro-seconds */
 110 
 111 static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name);
 112 static int pty_open_subsid(const char *prog, char *subsid_name);
 113 static int pty_child(const char *prog, int subsid, char *argv[]);
 114 static int pty_parent(const char *prog, int cntrl);
 115 static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);
 116 static GL_FD_EVENT_FN(pty_read_from_program);
 117 static int pty_write_to_fd(int fd, const char *string, int n);
 118 static void pty_child_exited(int sig);
 119 static int pty_manager_readable(int fd, long usec);
 120 
 121 /*.......................................................................
 122  * Run a program with enhanced terminal editing facilities.
 123  *
 124  * Usage:
 125  *  enhance program [args...]
 126  */
 127 int main(int argc, char *argv[])
 128 {
 129   int cntrl = -1;  /* The fd of the pseudo-terminal controller device */
 130   int subsid = -1;  /* The fd of the pseudo-terminal subsidiary device */
 131   pid_t pid;       /* The return value of fork() */
 132   int status;      /* The return statuses of the parent and child functions */
 133   char subsid_name[PTY_MAX_NAME]; /* The filename of the subsidiary end of */
 134                                  /*  the pseudo-terminal. */
 135   char *prog;      /* The name of the program (ie. argv[0]) */
 136 /*
 137  * Check the arguments.
 138  */
 139   if(argc < 2) {
 140     fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
 141     return 1;
 142   };
 143 /*
 144  * Get the name of the program.
 145  */
 146   prog = argv[0];
 147 /*
 148  * If the user has the LC_CTYPE or LC_ALL environment variables set,
 149  * enable display of characters corresponding to the specified locale.
 150  */
 151   (void) setlocale(LC_CTYPE, "");
 152 /*
 153  * If the program is taking its input from a pipe or a file, or
 154  * sending its output to something other than a terminal, run the
 155  * program without tecla.
 156  */
 157   if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
 158     if(execvp(argv[1], argv + 1) < 0) {
 159       fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
 160               strerror(errno));
 161       fflush(stderr);
 162       _exit(1);
 163     };
 164   };
 165 /*
 166  * Open the manager side of a pseudo-terminal pair, and return
 167  * the corresponding file descriptor and the filename of the
 168  * subsidiary end of the pseudo-terminal.
 169  */
 170   if(pty_open_manager(prog, &cntrl, subsid_name))
 171     return 1;
 172 /*
 173  * Set up a signal handler to watch for the child process exiting.
 174  */
 175   signal(SIGCHLD, pty_child_exited);
 176 /*
 177  * The above signal handler sends the parent process a SIGINT signal.
 178  * This signal is caught by gl_get_line(), which resets the terminal
 179  * settings, and if the application signal handler for this signal
 180  * doesn't abort the process, gl_get_line() returns NULL with errno
 181  * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
 182  * returns and we have a chance to cleanup.
 183  */
 184   signal(SIGINT, SIG_IGN);
 185 /*
 186  * We will read user input in one process, and run the user's program
 187  * in a child process.
 188  */
 189   pid = fork();
 190   if(pid < 0) {
 191     fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
 192             strerror(errno));
 193     return 1;
 194   };
 195 /*
 196  * Are we the parent?
 197  */
 198   if(pid!=0) {
 199     status = pty_parent(prog, cntrl);
 200     close(cntrl);
 201   } else {
 202     close(cntrl); /* The child doesn't use the subsidiary device */
 203     signal(SIGCHLD, pty_child_exited);
 204     if((subsid = pty_open_subsid(prog, subsid_name)) >= 0) {
 205       status = pty_child(prog, subsid, argv + 1);
 206       close(subsid);
 207     } else {
 208       status = 1;
 209     };
 210   };
 211   return status;
 212 }
 213 
 214 /*.......................................................................
 215  * Open the manager side of a pseudo-terminal pair, and return
 216  * the corresponding file descriptor and the filename of the
 217  * subsidiary end of the pseudo-terminal.
 218  *
 219  * Input/Output:
 220  *  prog  const char *  The name of this program.
 221  *  cntrl        int *  The file descriptor of the pseudo-terminal
 222  *                      controller device will be assigned tp *cntrl.
 223  *  subsid_name  char *  The file-name of the pseudo-terminal subsidiary device
 224  *                      will be recorded in subsid_name[], which must have
 225  *                      at least PTY_MAX_NAME elements.
 226  * Output:
 227  *  return       int    0 - OK.
 228  *                      1 - Error.
 229  */
 230 static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name)
 231 {
 232   char manager_name[PTY_MAX_NAME]; /* The filename of the manager device */
 233   DIR *dir;                       /* The directory iterator */
 234   struct dirent *file;            /* A file in "/dev" */
 235 /*
 236  * Mark the controller device as not opened yet.
 237  */
 238   *cntrl = -1;
 239 /*
 240  * On systems with the Sys-V pseudo-terminal interface, we don't
 241  * have to search for a free manager terminal. We just open /dev/ptmx,
 242  * and if there is a free manager terminal device, we are given a file
 243  * descriptor connected to it.
 244  */
 245 #if HAVE_SYSV_PTY
 246   *cntrl = open("/dev/ptmx", O_RDWR);
 247   if(*cntrl >= 0) {
 248 /*
 249  * Get the filename of the subsidiary side of the pseudo-terminal.
 250  */
 251     char *name = ptsname(*cntrl);
 252     if(name) {
 253       if(strlen(name)+1 > PTY_MAX_NAME) {
 254         fprintf(stderr, "%s: Subsidiary pty filename too long.\n", prog);
 255         return 1;
 256       };
 257       strlcpy(subsid_name, name, PTY_MAX_NAME);
 258 /*
 259  * If unable to get the subsidiary name, discard the controller file
 260  * descriptor, ready to try a search instead.
 261  */
 262     } else {
 263       close(*cntrl);
 264       *cntrl = -1;
 265     };
 266   } else {
 267 #endif
 268 /*
 269  * On systems without /dev/ptmx, or if opening /dev/ptmx failed,
 270  * we open one manager terminal after another, until one that isn't
 271  * in use by another program is found.
 272  *
 273  * Open the devices directory.
 274  */
 275     dir = opendir(PTY_DEV_DIR);
 276     if(!dir) {
 277       fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR,
 278               strerror(errno));
 279       return 1;
 280     };
 281 /*
 282  * Look for pseudo-terminal controller device files in the devices
 283  * directory.
 284  */
 285     while(*cntrl < 0 && (file = readdir(dir))) {
 286       if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {
 287 /*
 288  * Get the common extension of the control and subsidiary filenames.
 289  */
 290         const char *ext = file->d_name + sizeof(PTY_CNTRL)-1;
 291         if(strlen(ext) > PTY_MAX_SUFFIX)
 292           continue;
 293 /*
 294  * Attempt to open the control file.
 295  */
 296         strlcpy(manager_name, PTY_DEV_DIR, sizeof(manager_name));
 297         strlcat(manager_name, PTY_CNTRL, sizeof(manager_name));
 298         strlcat(manager_name, ext, sizeof(manager_name));
 299         *cntrl = open(manager_name, O_RDWR);
 300         if(*cntrl < 0)
 301           continue;
 302 /*
 303  * Attempt to open the matching subsidiary file.
 304  */
 305         strlcpy(subsid_name, PTY_DEV_DIR, PTY_MAX_NAME);
 306         strlcat(subsid_name, PTY_SUBSID, PTY_MAX_NAME);
 307         strlcat(subsid_name, ext, PTY_MAX_NAME);
 308       };
 309     };
 310     closedir(dir);
 311 #if HAVE_SYSV_PTY
 312   };
 313 #endif
 314 /*
 315  * Did we fail to find a pseudo-terminal pair that we could open?
 316  */
 317   if(*cntrl < 0) {
 318     fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog);
 319     return 1;
 320   };
 321 /*
 322  * System V systems require the program that opens the manager to
 323  * grant access to the subsidiary side of the pseudo-terminal.
 324  */
 325 #ifdef HAVE_SYSV_PTY
 326   if(grantpt(*cntrl) < 0 ||
 327      unlockpt(*cntrl) < 0) {
 328     fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
 329             strerror(errno));
 330     return 1;
 331   };
 332 #endif
 333 /*
 334  * Success.
 335  */
 336   return 0;
 337 }
 338 
 339 /*.......................................................................
 340  * Open the subsidiary end of a pseudo-terminal.
 341  *
 342  * Input:
 343  *  prog   const char *  The name of this program.
 344  *  subsid_name   char *  The filename of the subsidiary device.
 345  * Output:
 346  *  return        int    The file descriptor of the successfully opened
 347  *                       subsidiary device, or < 0 on error.
 348  */
 349 static int pty_open_subsid(const char *prog, char *subsid_name)
 350 {
 351   int fd;  /* The file descriptor of the subsidiary device */
 352 /*
 353  * Place the process in its own process group. In system-V based
 354  * OS's, this ensures that when the pseudo-terminal is opened, it
 355  * becomes the controlling terminal of the process.
 356  */
 357   if(setsid() < 0) {
 358     fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog,
 359             strerror(errno));
 360     return -1;
 361   };
 362 /*
 363  * Attempt to open the specified device.
 364  */
 365   fd = open(subsid_name, O_RDWR);
 366   if(fd < 0) {
 367     fprintf(stderr, "%s: Unable to open pty subsidiary device (%s).\n",
 368             prog, strerror(errno));
 369     return -1;
 370   };
 371 /*
 372  * On system-V streams based systems, we need to push the stream modules
 373  * that implement pseudo-terminal and termio interfaces. At least on
 374  * Solaris, which pushes these automatically when a subsidiary is opened,
 375  * this is redundant, so ignore errors when pushing the modules.
 376  */
 377 #if HAVE_SYSV_PTY
 378   (void) ioctl(fd, I_PUSH, "ptem");
 379   (void) ioctl(fd, I_PUSH, "ldterm");
 380 /*
 381  * On BSD based systems other than SunOS 4.x, the following makes the
 382  * pseudo-terminal the controlling terminal of the child process.
 383  * According to the pseudo-terminal example code in Steven's
 384  * Advanced programming in the unix environment, the !defined(CIBAUD)
 385  * part of the clause prevents this from being used under SunOS. Since
 386  * I only have his code with me, and won't have access to the book,
 387  * I don't know why this is necessary.
 388  */
 389 #elif defined(TIOCSCTTY) && !defined(CIBAUD)
 390   if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) {
 391     fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n",
 392             prog, strerror(errno));
 393     close(fd);
 394     return -1;


 479  */
 480   gl = del_GetLine(gl);
 481 /*
 482  * Delete the read buffer.
 483  */
 484   if(rbuff)
 485     free(rbuff);
 486 /*
 487  * Wait for the user's program to end.
 488  */
 489   (void) wait(&status);
 490 /*
 491  * Return either our error status, or the return status of the child
 492  * program.
 493  */
 494   return waserr ? 1 : status;
 495 }
 496 
 497 /*.......................................................................
 498  * Run the user's program, with its stdin and stdout connected to the
 499  * subsidiary end of the psuedo-terminal.
 500  *
 501  * Input:
 502  *  prog  const char *   The name of this program.
 503  *  subsid        int     The file descriptor of the subsidiary end of the
 504  *                       pseudo terminal.
 505  *  argv        char *[] The argument vector to pass to the user's program,
 506  *                       where argv[0] is the name of the user's program,
 507  *                       and the last argument is followed by a pointer
 508  *                       to NULL.
 509  * Output:
 510  *  return   int         If this function returns at all, an error must
 511  *                       have occured when trying to overlay the process
 512  *                       with the user's program. In this case 1 is
 513  *                       returned.
 514  */
 515 static int pty_child(const char *prog, int subsid, char *argv[])
 516 {
 517   struct termios attr; /* The terminal attributes */
 518 /*
 519  * We need to stop the pseudo-terminal from echoing everything that we send it.
 520  */
 521   if(tcgetattr(subsid, &attr)) {
 522     fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog,
 523             strerror(errno));
 524     return 1;
 525   };
 526   attr.c_lflag &= ~(ECHO);
 527   while(tcsetattr(subsid, TCSADRAIN, &attr)) {
 528     if(errno != EINTR) {
 529       fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno));
 530       return 1;
 531     };
 532   };
 533 /*
 534  * Arrange for stdin, stdout and stderr to be connected to the subsidiary
 535  * device, ignoring errors that imply that either stdin or stdout is closed.
 536  */
 537   while(dup2(subsid, STDIN_FILENO) < 0 && errno==EINTR)
 538     ;
 539   while(dup2(subsid, STDOUT_FILENO) < 0 && errno==EINTR)
 540     ;
 541   while(dup2(subsid, STDERR_FILENO) < 0 && errno==EINTR)
 542     ;
 543 /*
 544  * Run the user's program.
 545  */
 546   if(execvp(argv[0], argv) < 0) {
 547     fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0],
 548             strerror(errno));
 549     fflush(stderr);
 550     _exit(1);
 551   };
 552   return 0;  /* This should never be reached */
 553 }
 554 
 555 /*.......................................................................
 556  * This is the event-handler that is called by gl_get_line() whenever
 557  * there is tet waiting to be read from the user's program, via the
 558  * controller end of the pseudo-terminal. See libtecla.h for details
 559  * about its arguments.
 560  */
 561 static GL_FD_EVENT_FN(pty_read_from_program)


 629         (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1);
 630 /*
 631  * Write everything up to the last newline to stdout.
 632  */
 633       (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff);
 634 /*
 635  * Start a new line.
 636  */
 637       (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2);
 638 /*
 639  * Skip trailing carriage returns and newlines.
 640  */
 641       while(*nextp=='\n' || *nextp=='\r')
 642         nextp++;
 643 /*
 644  * Move any unwritten text following the newline, to the start of the
 645  * buffer.
 646  */
 647       memmove(rbuff, nextp, len - (nextp - rbuff) + 1);
 648     };
 649   } while(pty_manager_readable(fd, PTY_READ_TIMEOUT));
 650 /*
 651  * Make the incomplete line in the output buffer the current prompt.
 652  */
 653   gl_replace_prompt(gl, rbuff);
 654   return GLFD_REFRESH;
 655 }
 656 
 657 /*.......................................................................
 658  * Write a given string to a specified file descriptor.
 659  *
 660  * Input:
 661  *  fd             int     The file descriptor to write to.
 662  *  string  const char *   The string to write (of at least 'n' characters).
 663  *  n              int     The number of characters to write.
 664  * Output:
 665  *  return         int     0 - OK.
 666  *                         1 - Error.
 667  */
 668 static int pty_write_to_fd(int fd, const char *string, int n)
 669 {
 670   int ndone = 0;  /* The number of characters written so far */
 671 /*
 672  * Do as many writes as are needed to write the whole string.
 673  */
 674   while(ndone < n) {
 675     int nnew = write(fd, string + ndone, n - ndone);
 676     if(nnew > 0)
 677       ndone += nnew;
 678     else if(errno != EINTR)
 679       return 1;
 680   };
 681   return 0;
 682 }
 683 
 684 /*.......................................................................
 685  * This is the signal handler that is called when the child process
 686  * that is running the user's program exits for any reason. It closes
 687  * the subsidiary end of the terminal, so that gl_get_line() in the parent
 688  * process sees an end of file.
 689  */
 690 static void pty_child_exited(int sig)
 691 {
 692   raise(SIGINT);
 693 }
 694 
 695 /*.......................................................................
 696  * Return non-zero after a given amount of time if there is data waiting
 697  * to be read from a given file descriptor.
 698  *
 699  * Input:
 700  *  fd        int  The descriptor to watch.
 701  *  usec     long  The number of micro-seconds to wait for input to
 702  *                 arrive before giving up.
 703  * Output:
 704  *  return    int  0 - No data is waiting to be read (or select isn't
 705  *                     available).
 706  *                 1 - Data is waiting to be read.
 707  */
 708 static int pty_manager_readable(int fd, long usec)
 709 {
 710 #if HAVE_SELECT
 711   fd_set rfds;             /* The set of file descriptors to check */
 712   struct timeval timeout;  /* The timeout */
 713   FD_ZERO(&rfds);
 714   FD_SET(fd, &rfds);
 715   timeout.tv_sec = 0;
 716   timeout.tv_usec = usec;
 717   return select(fd+1, &rfds, NULL, NULL, &timeout) == 1;
 718 #else
 719   return 0;
 720 #endif
 721 }