WvStreams
wvpty.cc
1 /* -*- Mode: C++ -*-
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
4  *
5  * WvStreams implementation of ptys under Linux.
6  *
7  * For more information on programming ptys, see chapter 19 of
8  * Stevens' "Advanced Programming in the UNIX Environment"
9  */
10 
11 #include "wvpty.h"
12 
13 #include <grp.h>
14 #include <termios.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <sys/signal.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 
22 #define DPRINTF(format, args...)
23 //#define DPRINTF(format, args...) fprintf(stderr, "WvPty:" format, ##args)
24 
25 bool WvPty::open_pty(WvString &master, int &master_fd,
26  WvString &slave, int &slave_fd)
27 {
28  const char *xvals = "pqrstuvwxyzPQRST";
29  const char *yvals = "0123456789abcdef";
30  char pty[] = "/dev/ptyXY";
31  char tty[] = "/dev/ttyXY";
32 
33  for (int i=0; xvals[i]; ++i)
34  {
35  pty[8] = tty[8] = xvals[i];
36 
37  for (int j=0; yvals[j]; ++j)
38  {
39  pty[9] = tty[9] = yvals[j];
40 
41  master_fd = ::open(pty, O_RDWR);
42  if (master_fd >= 0)
43  slave_fd = ::open(tty, O_RDWR);
44  else slave_fd = -1;
45  if (master_fd < 0 || slave_fd < 0)
46  {
47  int saved_errno = errno;
48  if (master_fd >= 0) ::close(master_fd);
49  if (slave_fd >= 0) ::close(slave_fd);
50  if (saved_errno == ENOENT)
51  {
52  DPRINTF("No more PTYs (ENOENT)\n");
53  return false; // no more ptys
54  }
55  }
56  else
57  {
58  DPRINTF("PTY is %s\n", (master = WvString(pty)).edit());
59  DPRINTF("TTY is %s\n", (slave = WvString(tty)).edit());
60 
61  // try to change owner and permissions of slave.
62  // this will only work if we
63  // are root; if we're not root, we don't care.
64  struct group *gr = ::getgrnam("tty");
65  ::fchown(slave_fd, ::getuid(), gr? gr->gr_gid: (gid_t)-1);
66  ::fchmod(slave_fd, S_IRUSR | S_IWUSR | S_IWGRP);
67 
68  return true;
69  }
70  }
71  }
72 
73  DPRINTF("No more PTYs\n");
74  return false;
75 }
76 
77 WvPty::WvPty(const char *program, const char * const *argv,
78  Callback _pre_exec_cb, Callback _post_exec_cb)
79  : _pid(-1), _exit_status(242),
80  pre_exec_cb(_pre_exec_cb), post_exec_cb(_post_exec_cb)
81 {
82  int master_fd, slave_fd;
83  if (!open_pty(_master, master_fd, _slave, slave_fd)
84  || (_pid = ::fork()) < 0)
85  {
86  // error
87  _pid = -1;
88  setfd(-1);
89  }
90  else if (_pid == 0)
91  {
92  // child
93  static const int std_fds[] = {
94  STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, -1
95  };
96  const int *std_fd;
97 
98  if (::close(master_fd) < 0)
99  {
100  DPRINTF("close(master_fd) failed: %s\n", strerror(errno));
101  goto _error;
102  }
103  if (::setsid() < 0)
104  {
105  DPRINTF("setsid() failed: %s\n", strerror(errno));
106  goto _error;
107  }
108  ::ioctl(slave_fd, TIOCSCTTY, NULL); // This may fail in case opening the
109  // ptys in open_slave proactively gave us a
110  // controling terminal
111  for (std_fd = std_fds; *std_fd != -1; ++std_fd)
112  {
113  if (::dup2(slave_fd, *std_fd) < 0)
114  {
115  DPRINTF("dup2(slave_fd, %s) failed: %s\n", *std_fd,
116  strerror(errno));
117  goto _error;
118  }
119  }
120  if (slave_fd > STDERR_FILENO && ::close(slave_fd) < 0)
121  {
122  DPRINTF("close(slave_fd) failed: %s\n", strerror(errno));
123  goto _error;
124  }
125 
126  for (std_fd = std_fds; *std_fd != -1; ++std_fd)
127  {
128  if (::fcntl(*std_fd, F_SETFL,
129  fcntl(*std_fd, F_GETFL) & (O_APPEND|O_ASYNC)))
130  {
131  DPRINTF("fcntl(%s, F_SETFL) failed: %s\n", *std_fd,
132  strerror(errno));
133  goto _error;
134  }
135  }
136 
137  if (pre_exec_cb && !pre_exec_cb(*this)) goto _error;
138  execvp(program, (char * const *)argv);
139  if (post_exec_cb) post_exec_cb(*this);
140 
141 _error:
142  _exit(242);
143  }
144  else
145  {
146  // parent
147  if (::close(slave_fd) < 0)
148  {
149  DPRINTF("close(slave_fd) failed: %s\n", strerror(errno));
150  goto _error;
151  }
152  setfd(master_fd);
153  }
154 }
155 
156 void WvPty::kill(int signum)
157 {
158  if (_pid != -1)
159  ::kill(_pid, signum);
160 }
161 
162 void WvPty::monitor_child(bool wait)
163 {
164  if (_pid != -1)
165  {
166  int status;
167  if (::waitpid(_pid, &status, wait? 0: WNOHANG) == _pid)
168  {
169  _pid = -1;
170  _exit_status = status;
171  }
172  }
173 }
174 
175 bool WvPty::child_exited()
176 {
177  monitor_child(false);
178  return _pid == -1;
179 }
180 
181 bool WvPty::child_killed()
182 {
183  monitor_child(false);
184  return _pid == -1 && WIFSIGNALED(_exit_status);
185 }
186 
187 int WvPty::finish()
188 {
189  monitor_child(true);
190  return WEXITSTATUS(_exit_status);
191 }
192 
193 int WvPty::exit_status()
194 {
195  monitor_child(false);
196  if (_pid == -1)
197  {
198  if (child_killed())
199  return WTERMSIG(_exit_status);
200  else
201  return WEXITSTATUS(_exit_status);
202  }
203  else
204  return 242;
205 }
206 
WvStreamClone::close
virtual void close()
Close this stream.
Definition: wvstreamclone.cc:83
WvErrorBase::strerror
static WvString strerror(int errnum)
A replacement for the operating system ::strerror() function that can map more kinds of error strings...
Definition: wverror.cc:91
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
group
Definition: argp-parse.c:204
WvFdStream::close
virtual void close()
Closes the file descriptors.
Definition: wvfdstream.cc:117