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