mrrl

Minimal Reliable Reproducible Linux
git clone https://ccx.te2000.cz/git/mrrl
Log | Files | Refs | Submodules | README

spawn-pty.c (3685B)


      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/strerr2.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 (ptmx_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     // # parent
    100     // env['PTMX_FD'] = "0"
    101     // execve(terminal, env)
    102     char envstr[9 + strlen(pts_name)];
    103     memcpy(envstr, "PTS_NAME=", 9);
    104     strcpy(&envstr[8], term_env);
    105     xmexec_n(ptmx_argv, envstr, strlen(envstr), 1);
    106   }
    107 }
    108 
    109 typedef const char * arg_t;
    110 
    111 int main(const int argc, const char **argv) {
    112   if(argc < 2) {
    113     dieusage();
    114   }
    115   arg_t pty_argv[argc];
    116   const char *term_env = argv[1];
    117   int n = 2;
    118   if(term_env[0] == ' ') {
    119     dieusage();
    120   }
    121   if(argv[2][0] != ' ') {
    122     strerr_dieusage(100, USAGE "\nerror: missing block");
    123   }
    124   for(int n = 2; n < argc - 1; n++) {
    125     switch(argv[n][0]) {
    126       case 0:
    127         pty_argv[n - 2] = 0;
    128         exec_terminal(term_env, &argv[n + 1], pty_argv);
    129         return 111;
    130       case ' ':
    131         pty_argv[n - 2] = &argv[n][1];
    132         break;
    133       default:
    134         strerr_dieusage(100, USAGE "\nerror: improperly terminated block");
    135         return 100;
    136     }
    137   }
    138   strerr_dieusage(100, USAGE "\nerror: unterminated block");
    139 }
    140 
    141 /*  vim: ft=c sts=2 sw=2 et
    142 */