1 // common/SubProcess.cc
2 // This file is part of SlugTerm; see http://chezphil.org/slugterm
3 // (C) 2006 Philip Endecott
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "SubProcess.hh"
20
21 // For forkpty():
22 #if defined(__FreeBSD__)
23 #include <libutil.h>
24 #include <sys/ioctl.h>
25 #include <termios.h>
26 #elif defined(__OpenBSD__)
27 #include <termios.h>
28 #include <util.h>
29 #elif defined(__APPLE__)
30 #include <util.h>
31 #else
32 #include <pty.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <signal.h>
39 #include <sys/wait.h>
40
41 #include <boost/bind.hpp>
42
43 #include "select.hh"
44 #include "Exception.hh"
45
46
47 using namespace std;
48 using namespace pbe;
49
50
open_subprocess(string command,int pty_rows,int pty_cols)51 static std::pair<int,::pid_t> open_subprocess(string command, int pty_rows, int pty_cols)
52 {
53 struct winsize ws;
54 ws.ws_row=pty_rows;
55 ws.ws_col=pty_cols;
56 ws.ws_xpixel=0;
57 ws.ws_ypixel=0;
58
59 int fd;
60 int pid = forkpty(&fd, NULL, NULL, &ws);
61 if (pid==-1) {
62 throw_ErrnoException("forkpty()");
63 }
64 if (pid==0) {
65 setenv("TERM","linux",1);
66 struct termios t;
67 tcgetattr(0,&t); // Could fail, but where would we send the error?
68 t.c_cc[VERASE]=8; // Make ctrl-H (backspace) the erase character.
69 tcsetattr(0,TCSANOW,&t); // ditto.
70 execl("/bin/sh","/bin/sh","-c",command.c_str(),NULL);
71 throw_ErrnoException("execl(/bin/sh -c "+command+")"); // pointless.
72 }
73 return make_pair(fd,pid);
74 }
75
76
SubProcess(Activity::onOutput_t onOutput,Activity::onError_t onError,string command,int pty_rows,int pty_cols)77 SubProcess::SubProcess(Activity::onOutput_t onOutput,
78 Activity::onError_t onError,
79 string command,
80 int pty_rows, int pty_cols):
81 SubProcess_base(open_subprocess(command, pty_rows, pty_cols)),
82 Activity(onOutput, onError, SubProcess_base::fd)
83 {}
84
85
~SubProcess()86 SubProcess::~SubProcess()
87 {
88 // We do two things to try to kill the subprocess: we close the fd,
89 // which really ought to kill it, and we SIGHUP it. The SIGHUP
90 // by itself may not be sufficient if the process is nohup or
91 // setuid or something. The close by itself really should be
92 // sufficient, but I'm keeping the SIGHUP because I'm paranoid.
93 // The three results that we want are (a) the process dies,
94 // (b) it does not become a zombie, and (c) the output processor
95 // thread terminates so that ~Activity can join it. For (c),
96 // we hope that it will get an error when reading from the fd
97 // and/or that it sees terminating set. There's a danger that
98 // we could close the fd and something else could open another
99 // fd with the same number, which the output processor could read.
100 // I hope that's not a high probability.
101
102 terminating = true;
103
104 fd_open = false;
105 try {
106 SubProcess_base::fd.close();
107 // fd.close() can throw.
108 } catch (...) {}
109 kill(pid,SIGHUP);
110 }
111
112
113