00001
00002
00003
00004
00005
00006
00007
00008
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
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;
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
00062
00063
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
00087 _pid = -1;
00088 setfd(-1);
00089 }
00090 else if (_pid == 0)
00091 {
00092
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);
00109
00110
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
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