ccx-utils

Miscellaneous utilities written in C
git clone https://ccx.te2000.cz/git/ccx-utils
Log | Files | Refs

spawn-pty.c (3610B)


      1 #include <pty.h>
      2 #include <fcntl.h>
      3 #include <signal.h>
      4 #include <errno.h>
      5 #include <unistd.h>  /* fork(), getpid(), setsid(), tcsetpgrp() */
      6 #include <stdlib.h>  /* grantpt(), unlockpt(), ptsname() */
      7 
      8 #include <skalibs/exec.h>
      9 #include <skalibs/djbunix.h>
     10 #include <skalibs/strerr.h>
     11 
     12 #define PROG "spawn-pty"
     13 #define USAGE "spawn-pty term_name { pty-prog ... } ptmx-prog ..."
     14 #define dieusage() strerr_dieusage(100, USAGE)
     15 
     16 #define resetsig(S) if(signal(S, SIG_DFL) == SIG_ERR) { \
     17       strerr_dief1sys(111, "resetting signal handler"); \
     18       }
     19 
     20 void exec_terminal(const char *term_env, const char **ptmx_argv, const char **pty_argv) {
     21   int ptmx_fd;
     22   const char *pts_name;
     23 
     24   ptmx_fd = posix_openpt(O_RDWR | O_NOCTTY);
     25   if (ptmx_fd < 0) {
     26     strerr_dief1sys(111, "posix_openpt()");
     27   }
     28   if (grantpt(ptmx_fd) != 0) {
     29     strerr_dief1sys(111, "grantpt()");
     30   }
     31   if (unlockpt(ptmx_fd) != 0) {
     32     strerr_dief1sys(111, "unlockpt()");
     33   }
     34   pts_name = ptsname(ptmx_fd);
     35   if (pts_name == NULL) {
     36     strerr_dief1sys(111, "ptsname()");
     37   }
     38 
     39   int fork_pid = fork();
     40   if(fork_pid < 0) {
     41     strerr_dief1sys(111, "fork()");
     42   }
     43   if(fork_pid == 0) {
     44     /* child */
     45     int pty_fd;
     46 
     47     resetsig(SIGHUP);
     48     resetsig(SIGINT);
     49     resetsig(SIGQUIT);
     50     resetsig(SIGPIPE);
     51     resetsig(SIGALRM);
     52     resetsig(SIGTERM);
     53     resetsig(SIGCHLD);
     54     resetsig(SIGCONT);
     55     resetsig(SIGTSTP);
     56     resetsig(SIGTTIN);
     57     resetsig(SIGTTOU);
     58 
     59     if(close(ptmx_fd) != 0) {
     60       strerr_dief1sys(111, "close(ptmx_fd) in child");
     61     }
     62 
     63     if(setsid() < 0) {  /* create new session */
     64       strerr_dief1sys(111, "setsid");
     65     }
     66     /* open without O_NOCTTY so controlling terminal gets set */
     67     pty_fd = open(pts_name, O_RDWR);
     68     if (pty_fd < 0) {
     69       strerr_dief1sys(111, "open(pty)");
     70     }
     71     if (tcsetpgrp(pty_fd, getpid()) < 0) {  /* set controlling terminal */
     72       strerr_dief1sys(111, "tcsetpgrp");
     73     }
     74 
     75     if(dup2(pty_fd, 0) < 0) {
     76       strerr_dief1sys(111, "dup2(pty_fd, 0) in child");
     77     }
     78     if(dup2(pty_fd, 1) < 0) {
     79       strerr_dief1sys(111, "dup2(pty_fd, 1) in child");
     80     }
     81     if(dup2(pty_fd, 2) < 0) {
     82       strerr_dief1sys(111, "dup2(pty_fd, 2) in child");
     83     }
     84     if(close(pty_fd) != 0) {
     85       strerr_dief1sys(111, "close(pty_fd) in child");
     86     }
     87     char envstr[6 + strlen(term_env)];
     88     memcpy(envstr, "TERM=", 5);
     89     strcpy(&envstr[5], term_env);
     90     xmexec_n(pty_argv, envstr, strlen(envstr), 1);
     91   } else {
     92     /* parent */
     93     if(dup2(ptmx_fd, 0) < 0) {
     94       strerr_dief1sys(111, "dup2(ptmx_fd, 0) in parent");
     95     }
     96     if(close(ptmx_fd) != 0) {
     97       strerr_dief1sys(111, "close(ptmx_fd) in parent");
     98     }
     99     char envstr[9 + strlen(pts_name)];
    100     memcpy(envstr, "PTS_NAME=", 9);
    101     strcpy(&envstr[8], term_env);
    102     xmexec_n(ptmx_argv, envstr, strlen(envstr), 1);
    103   }
    104 }
    105 
    106 typedef const char * arg_t;
    107 
    108 int main(const int argc, const char **argv) {
    109   if(argc < 2) {
    110     dieusage();
    111   }
    112   arg_t pty_argv[argc];
    113   const char *term_env = argv[1];
    114   int n = 2;
    115   if(term_env[0] == ' ') {
    116     dieusage();
    117   }
    118   if(argv[2][0] != ' ') {
    119     strerr_dieusage(100, USAGE "\nerror: missing block");
    120   }
    121   for(int n = 2; n < argc - 1; n++) {
    122     switch(argv[n][0]) {
    123       case 0:
    124         pty_argv[n - 2] = 0;
    125         exec_terminal(term_env, &argv[n + 1], pty_argv);
    126         return 111;
    127       case ' ':
    128         pty_argv[n - 2] = &argv[n][1];
    129         break;
    130       default:
    131         strerr_dieusage(100, USAGE "\nerror: improperly terminated block");
    132         return 100;
    133     }
    134   }
    135   strerr_dieusage(100, USAGE "\nerror: unterminated block");
    136 }
    137 
    138 /*  vim: ft=c sts=2 sw=2 et
    139 */