WvStreams
|
00001 /* -*- Mode: C++ -*- 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2004 Net Integration Technologies, Inc. 00004 * 00005 * WvStreams implementation of ptys under Linux. 00006 * 00007 * For more information on programming ptys, see chapter 19 of 00008 * Stevens' "Advanced Programming in the UNIX Environment" 00009 */ 00010 00011 #include "wvpty.h" 00012 00013 #include <grp.h> 00014 #include <termios.h> 00015 #include <fcntl.h> 00016 #include <sys/ioctl.h> 00017 #include <sys/signal.h> 00018 #include <sys/types.h> 00019 #include <sys/wait.h> 00020 #include <sys/stat.h> 00021 00022 #define DPRINTF(format, args...) 00023 //#define DPRINTF(format, args...) fprintf(stderr, "WvPty:" format, ##args) 00024 00025 bool WvPty::open_pty(WvString &master, int &master_fd, 00026 WvString &slave, int &slave_fd) 00027 { 00028 const char *xvals = "pqrstuvwxyzPQRST"; 00029 const char *yvals = "0123456789abcdef"; 00030 char pty[] = "/dev/ptyXY"; 00031 char tty[] = "/dev/ttyXY"; 00032 00033 for (int i=0; xvals[i]; ++i) 00034 { 00035 pty[8] = tty[8] = xvals[i]; 00036 00037 for (int j=0; yvals[j]; ++j) 00038 { 00039 pty[9] = tty[9] = yvals[j]; 00040 00041 master_fd = ::open(pty, O_RDWR); 00042 if (master_fd >= 0) 00043 slave_fd = ::open(tty, O_RDWR); 00044 else slave_fd = -1; 00045 if (master_fd < 0 || slave_fd < 0) 00046 { 00047 int saved_errno = errno; 00048 if (master_fd >= 0) ::close(master_fd); 00049 if (slave_fd >= 0) ::close(slave_fd); 00050 if (saved_errno == ENOENT) 00051 { 00052 DPRINTF("No more PTYs (ENOENT)\n"); 00053 return false; // no more ptys 00054 } 00055 } 00056 else 00057 { 00058 DPRINTF("PTY is %s\n", (master = WvString(pty)).edit()); 00059 DPRINTF("TTY is %s\n", (slave = WvString(tty)).edit()); 00060 00061 // try to change owner and permissions of slave. 00062 // this will only work if we 00063 // are root; if we're not root, we don't care. 00064 struct group *gr = ::getgrnam("tty"); 00065 ::fchown(slave_fd, ::getuid(), gr? gr->gr_gid: (gid_t)-1); 00066 ::fchmod(slave_fd, S_IRUSR | S_IWUSR | S_IWGRP); 00067 00068 return true; 00069 } 00070 } 00071 } 00072 00073 DPRINTF("No more PTYs\n"); 00074 return false; 00075 } 00076 00077 WvPty::WvPty(const char *program, const char * const *argv, 00078 Callback _pre_exec_cb, Callback _post_exec_cb) 00079 : _pid(-1), _exit_status(242), 00080 pre_exec_cb(_pre_exec_cb), post_exec_cb(_post_exec_cb) 00081 { 00082 int master_fd, slave_fd; 00083 if (!open_pty(_master, master_fd, _slave, slave_fd) 00084 || (_pid = ::fork()) < 0) 00085 { 00086 // error 00087 _pid = -1; 00088 setfd(-1); 00089 } 00090 else if (_pid == 0) 00091 { 00092 // child 00093 static const int std_fds[] = { 00094 STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, -1 00095 }; 00096 const int *std_fd; 00097 00098 if (::close(master_fd) < 0) 00099 { 00100 DPRINTF("close(master_fd) failed: %s\n", strerror(errno)); 00101 goto _error; 00102 } 00103 if (::setsid() < 0) 00104 { 00105 DPRINTF("setsid() failed: %s\n", strerror(errno)); 00106 goto _error; 00107 } 00108 ::ioctl(slave_fd, TIOCSCTTY, NULL); // This may fail in case opening the 00109 // ptys in open_slave proactively gave us a 00110 // controling terminal 00111 for (std_fd = std_fds; *std_fd != -1; ++std_fd) 00112 { 00113 if (::dup2(slave_fd, *std_fd) < 0) 00114 { 00115 DPRINTF("dup2(slave_fd, %s) failed: %s\n", *std_fd, 00116 strerror(errno)); 00117 goto _error; 00118 } 00119 } 00120 if (slave_fd > STDERR_FILENO && ::close(slave_fd) < 0) 00121 { 00122 DPRINTF("close(slave_fd) failed: %s\n", strerror(errno)); 00123 goto _error; 00124 } 00125 00126 for (std_fd = std_fds; *std_fd != -1; ++std_fd) 00127 { 00128 if (::fcntl(*std_fd, F_SETFL, 00129 fcntl(*std_fd, F_GETFL) & (O_APPEND|O_ASYNC))) 00130 { 00131 DPRINTF("fcntl(%s, F_SETFL) failed: %s\n", *std_fd, 00132 strerror(errno)); 00133 goto _error; 00134 } 00135 } 00136 00137 if (pre_exec_cb && !pre_exec_cb(*this)) goto _error; 00138 execvp(program, (char * const *)argv); 00139 if (post_exec_cb) post_exec_cb(*this); 00140 00141 _error: 00142 _exit(242); 00143 } 00144 else 00145 { 00146 // parent 00147 if (::close(slave_fd) < 0) 00148 { 00149 DPRINTF("close(slave_fd) failed: %s\n", strerror(errno)); 00150 goto _error; 00151 } 00152 setfd(master_fd); 00153 } 00154 } 00155 00156 void WvPty::kill(int signum) 00157 { 00158 if (_pid != -1) 00159 ::kill(_pid, signum); 00160 } 00161 00162 void WvPty::monitor_child(bool wait) 00163 { 00164 if (_pid != -1) 00165 { 00166 int status; 00167 if (::waitpid(_pid, &status, wait? 0: WNOHANG) == _pid) 00168 { 00169 _pid = -1; 00170 _exit_status = status; 00171 } 00172 } 00173 } 00174 00175 bool WvPty::child_exited() 00176 { 00177 monitor_child(false); 00178 return _pid == -1; 00179 } 00180 00181 bool WvPty::child_killed() 00182 { 00183 monitor_child(false); 00184 return _pid == -1 && WIFSIGNALED(_exit_status); 00185 } 00186 00187 int WvPty::finish() 00188 { 00189 monitor_child(true); 00190 return WEXITSTATUS(_exit_status); 00191 } 00192 00193 int WvPty::exit_status() 00194 { 00195 monitor_child(false); 00196 if (_pid == -1) 00197 { 00198 if (child_killed()) 00199 return WTERMSIG(_exit_status); 00200 else 00201 return WEXITSTATUS(_exit_status); 00202 } 00203 else 00204 return 242; 00205 } 00206