add support for asynchronous processes
[opensuse:smpppd.git] / smpppd / process.cc
1
2
3 /*
4  *  Author: Arvin Schnell <arvin@suse.de>
5  */
6
7
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13 #include <paths.h>
14
15 #include <csignal>
16 #include <cerrno>
17 #include <cstdarg>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21
22 #include "process.h"
23 #include "utils.h"
24
25 // SIGCHLD is blocked anyways
26 #define sigprocmask(x, y, z)
27
28 Processes processes;
29
30
31 Process::Process ()
32 {
33     dprintf ("%s\n", __PRETTY_FUNCTION__);
34
35     sigemptyset (&block_chld);
36     sigaddset (&block_chld, SIGCHLD);
37
38     clear ();
39
40     processes.push_back (this);
41 }
42
43
44 Process::~Process ()
45 {
46     dprintf ("%s\n", __PRETTY_FUNCTION__);
47     processes.remove (this);
48     delete exitcallback;
49 }
50
51
52 void
53 Process::clear ()
54 {
55     pid = 0;
56
57     running = false;
58     has_died_flag = false;
59
60     status = 0;
61     args.clear ();
62     fd_notclose.clear ();
63 }
64
65
66 bool
67 Process::start ()
68 {
69     const unsigned int nargs = args.size ();
70
71     if (nargs < 1)
72         return false;
73
74     // We have to block the child signal, otherwise the signal can
75     // arrive before the fork return and the pid is set. Then our
76     // signal handler would not work.
77     sigprocmask (SIG_BLOCK, &block_chld, 0);
78     running = true;
79     has_died_flag = false;
80     pid = fork ();
81     sigprocmask (SIG_UNBLOCK, &block_chld, 0);
82
83     if (pid == 0)
84     {
85         if(!debug_level)
86         {
87             for (int fd = 0; fd < 3; fd++) {
88                 int null_fd = open (_PATH_DEVNULL, O_RDONLY);
89                 dup2 (null_fd, fd);
90                 close (null_fd);
91             }
92         }
93
94         for (int tty = 3; tty < 256; tty++) {
95             bool doit = true;
96             for (unsigned int i = 0; i < fd_notclose.size (); i++)
97                 if (tty == fd_notclose[i])
98                     doit = false;
99             if (doit)
100                 close (tty);
101         }
102
103         char** ptr = new char* [nargs + 1];
104         for (unsigned int i = 0; i < nargs; i++)
105             ptr[i] = strdup (args[i].c_str ());
106         ptr[nargs] = 0;
107
108         // pppd may call kill (0, SIGTERM), but I don't want to die.
109         setsid ();
110
111         execvp (ptr[0], ptr);
112         _exit (EXIT_FAILURE);
113     }
114
115     if (pid < 0)
116     {
117         pid = 0;
118         running = false;
119         return false;
120     }
121
122     return true;
123 }
124
125
126 void
127 Process::close_stuff ()
128 {
129     for (unsigned int i = 0; i < fd_notclose.size (); i++)
130         close (fd_notclose[i]);
131 }
132
133
134 void
135 Process::sigterm ()
136 {
137     if (!running)
138         return;
139
140     pid_t ret = waitpid (pid, &status, WNOHANG);
141
142     if (ret == pid) {
143         running = false;
144         has_died_flag = true;
145         close_stuff ();
146     }
147 }
148
149
150 void
151 Process::kill (int timeout, bool group)
152 {
153     sigprocmask (SIG_BLOCK, &block_chld, 0);
154     if (running)
155         ::kill (!group ? pid : -pid, SIGTERM);
156     sigprocmask (SIG_UNBLOCK, &block_chld, 0);
157
158     if (!running)
159         return;
160
161     for (int i = 0; i < timeout; i++) {
162         if (!running)
163             return;
164         sleep (1);
165     }
166
167     sigprocmask (SIG_BLOCK, &block_chld, 0);
168     if (running)
169         ::kill (!group ? pid : -pid, SIGKILL);
170     sigprocmask (SIG_UNBLOCK, &block_chld, 0);
171 }
172
173
174 void
175 Process::wait_for_dead ()
176 {
177     sigprocmask (SIG_BLOCK, &block_chld, 0);
178     if (running) {
179         pid_t ret = waitpid (pid, &status, 0);
180         if (ret == pid) {
181             running = false;
182             has_died_flag = true;
183             close_stuff ();
184         }
185     }
186     sigprocmask (SIG_UNBLOCK, &block_chld, 0);
187 }
188
189
190 bool
191 Process::has_died ()
192 {
193     if (has_died_flag) {
194         has_died_flag = false;
195         return true;
196     } else {
197         return false;
198     }
199 }
200
201
202 bool
203 Process::normal_exit ()
204 {
205     int tmp = status;
206     return WIFEXITED (tmp);
207 }
208
209
210 int
211 Process::exit_status ()
212 {
213     int tmp = status;
214     return WEXITSTATUS (tmp);
215 }
216
217
218 Process&
219 Process::operator << (const char* arg)
220 {
221     args.push_back (arg);
222     return *this;
223 }
224
225
226 Process&
227 Process::operator << (const string& arg)
228 {
229     args.push_back (arg);
230     return *this;
231 }
232
233
234 Process&
235 Process::operator << (const vector <string>& arg)
236 {
237     for (vector <string>::const_iterator it = arg.begin ();
238          it != arg.end (); it++)
239         args.push_back (*it);
240     return *this;
241 }
242
243
244 Process&
245 Process::operator << (int num)
246 {
247     char buffer[32];
248     snprintf (buffer, 32, "%d", num);
249     args.push_back (buffer);
250     return *this;
251 }
252
253
254 void
255 Process::add_fd_notclose (int fd)
256 {
257     fd_notclose.push_back (fd);
258 }
259
260
261 string
262 Process::dump_args () const
263 {
264     string ret;
265
266     for (std::vector <string>::const_iterator it = args.begin ();
267          it != args.end (); it++) {
268         if (it != args.begin ())
269             ret += ' ';
270         ret += '"';
271         ret += (*it);
272         ret += '"';
273     }
274
275     return ret;
276 }
277
278
279 void
280 Processes::sigterm ()
281 {
282     // waitpid changes errno, so save it.
283
284     int saved_errno = errno;
285
286     for (const_iterator it = begin (); it != end (); it++)
287         (*it)->sigterm ();
288
289     errno = saved_errno;
290 }
291
292
293 void
294 Processes::terminated (pid_t pid)
295 {
296     for (const_iterator it = begin (); it != end (); it++)
297     {
298         if(pid == (*it)->pid)
299         {
300             dprintf("pid %d found\n", pid);
301             if ((*it)->exitcallback)
302                 (*(*it)->exitcallback)(*it);
303
304             return;
305         }
306     }
307     dprintf("pid %d NOT found\n", pid);
308 }
309
310 void
311 Process::set_exitcallback(ProcessExit* handler)
312 {
313     exitcallback = handler;
314 }