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 */