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