218df7604f5be20d22fd6485566867f183387013
[tinc] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
4                   2000-2003 Guus Sliepen <guus@sliepen.eu.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: process.c,v 1.1.2.57 2003/07/22 20:55:20 guus Exp $
21 */
22
23 #include "system.h"
24
25 #include "conf.h"
26 #include "connection.h"
27 #include "device.h"
28 #include "edge.h"
29 #include "logger.h"
30 #include "node.h"
31 #include "pidfile.h"
32 #include "process.h"
33 #include "subnet.h"
34 #include "utils.h"
35 #include "xalloc.h"
36
37 /* If zero, don't detach from the terminal. */
38 bool do_detach = true;
39 bool sighup = false;
40 bool sigalrm = false;
41
42 extern char *identname;
43 extern char *pidfilename;
44 extern char **g_argv;
45 extern bool use_logfile;
46
47 sigset_t emptysigset;
48
49 static int saved_debug_level = -1;
50
51 static void memory_full(int size)
52 {
53         logger(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size);
54         cp_trace();
55         exit(1);
56 }
57
58 /* Some functions the less gifted operating systems might lack... */
59
60 #ifndef HAVE_FCLOSEALL
61 static int fcloseall(void)
62 {
63         fflush(stdin);
64         fflush(stdout);
65         fflush(stderr);
66         fclose(stdin);
67         fclose(stdout);
68         fclose(stderr);
69         return 0;
70 }
71 #endif
72
73 /*
74   Close network connections, and terminate neatly
75 */
76 void cleanup_and_exit(int c)
77 {
78         cp();
79
80         close_network_connections();
81
82         ifdebug(CONNECTIONS)
83                 dump_device_stats();
84
85         logger(LOG_NOTICE, _("Terminating"));
86
87         closelogger();
88         exit(c);
89 }
90
91 /*
92   check for an existing tinc for this net, and write pid to pidfile
93 */
94 static bool write_pidfile(void)
95 {
96         int pid;
97
98         cp();
99
100         pid = check_pid(pidfilename);
101
102         if(pid) {
103                 if(netname)
104                         fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
105                                         netname, pid);
106                 else
107                         fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
108                 return false;
109         }
110
111         /* if it's locked, write-protected, or whatever */
112         if(!write_pid(pidfilename))
113                 return false;
114
115         return true;
116 }
117
118 /*
119   kill older tincd for this net
120 */
121 bool kill_other(int signal)
122 {
123         int pid;
124
125         cp();
126
127         pid = read_pid(pidfilename);
128
129         if(!pid) {
130                 if(netname)
131                         fprintf(stderr, _("No other tincd is running for net `%s'.\n"),
132                                         netname);
133                 else
134                         fprintf(stderr, _("No other tincd is running.\n"));
135                 return false;
136         }
137
138         errno = 0;                                      /* No error, sometimes errno is only changed on error */
139
140         /* ESRCH is returned when no process with that pid is found */
141         if(kill(pid, signal) && errno == ESRCH) {
142                 if(netname)
143                         fprintf(stderr, _("The tincd for net `%s' is no longer running. "),
144                                         netname);
145                 else
146                         fprintf(stderr, _("The tincd is no longer running. "));
147
148                 fprintf(stderr, _("Removing stale lock file.\n"));
149                 remove_pid(pidfilename);
150         }
151
152         return true;
153 }
154
155 /*
156   Detach from current terminal, write pidfile, kill parent
157 */
158 bool detach(void)
159 {
160         cp();
161
162         setup_signals();
163
164         /* First check if we can open a fresh new pidfile */
165
166         if(!write_pidfile())
167                 return false;
168
169         /* If we succeeded in doing that, detach */
170
171         closelogger();
172
173         if(do_detach) {
174                 if(daemon(0, 0)) {
175                         fprintf(stderr, _("Couldn't detach from terminal: %s"),
176                                         strerror(errno));
177                         return false;
178                 }
179
180                 /* Now UPDATE the pid in the pidfile, because we changed it... */
181
182                 if(!write_pid(pidfilename))
183                         return false;
184         }
185
186         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
187
188         logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
189                            VERSION, __DATE__, __TIME__, debug_level);
190
191         xalloc_fail_func = memory_full;
192
193         return true;
194 }
195
196 /*
197   Execute the program name, with sane environment.
198 */
199 static void _execute_script(const char *scriptname, char **envp)
200         __attribute__ ((noreturn));
201 static void _execute_script(const char *scriptname, char **envp)
202 {
203         int save_errno;
204
205         cp();
206
207         while(*envp)
208                 putenv(*envp++);
209
210         chdir("/");
211
212         closelogger();
213
214         /* Close all file descriptors */
215         fcloseall();
216
217         execl(scriptname, NULL);
218         /* No return on success */
219
220         save_errno = errno;
221
222         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
223         logger(LOG_ERR, _("Could not execute `%s': %s"), scriptname,
224                    strerror(save_errno));
225         exit(save_errno);
226 }
227
228 /*
229   Fork and execute the program pointed to by name.
230 */
231 bool execute_script(const char *name, char **envp)
232 {
233         pid_t pid;
234         int status;
235         struct stat s;
236         char *scriptname;
237
238         cp();
239
240         asprintf(&scriptname, "%s/%s", confbase, name);
241
242         /* First check if there is a script */
243
244         if(stat(scriptname, &s))
245                 return true;
246
247         pid = fork();
248
249         if(pid < 0) {
250                 logger(LOG_ERR, _("System call `%s' failed: %s"), "fork",
251                            strerror(errno));
252                 return false;
253         }
254
255         if(pid) {
256                 ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name);
257
258                 free(scriptname);
259
260                 if(waitpid(pid, &status, 0) == pid) {
261                         if(WIFEXITED(status)) { /* Child exited by itself */
262                                 if(WEXITSTATUS(status)) {
263                                         logger(LOG_ERR, _("Process %d (%s) exited with non-zero status %d"),
264                                                    pid, name, WEXITSTATUS(status));
265                                         return false;
266                                 } else
267                                         return true;
268                         } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
269                                 logger(LOG_ERR, _("Process %d (%s) was killed by signal %d (%s)"), pid,
270                                            name, WTERMSIG(status), strsignal(WTERMSIG(status)));
271                                 return false;
272                         } else {                        /* Something strange happened */
273                                 logger(LOG_ERR, _("Process %d (%s) terminated abnormally"), pid,
274                                            name);
275                                 return false;
276                         }
277                 } else if (errno != EINTR) {
278                         logger(LOG_ERR, _("System call `%s' failed: %s"), "waitpid",
279                                    strerror(errno));
280                         return false;
281                 }
282
283                 /* Why do we get EINTR? */
284                 return true;
285         }
286
287         /* Child here */
288
289         _execute_script(scriptname, envp);
290 }
291
292
293 /*
294   Signal handlers.
295 */
296
297 static RETSIGTYPE sigterm_handler(int a)
298 {
299         logger(LOG_NOTICE, _("Got TERM signal"));
300
301         cleanup_and_exit(0);
302 }
303
304 static RETSIGTYPE sigquit_handler(int a)
305 {
306         logger(LOG_NOTICE, _("Got QUIT signal"));
307         cleanup_and_exit(0);
308 }
309
310 static RETSIGTYPE fatal_signal_square(int a)
311 {
312         logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
313                    strsignal(a));
314         cp_trace();
315         exit(1);
316 }
317
318 static RETSIGTYPE fatal_signal_handler(int a)
319 {
320         struct sigaction act;
321         logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
322         cp_trace();
323
324         if(do_detach) {
325                 logger(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
326
327                 act.sa_handler = fatal_signal_square;
328                 act.sa_mask = emptysigset;
329                 act.sa_flags = 0;
330                 sigaction(SIGSEGV, &act, NULL);
331
332                 close_network_connections();
333                 sleep(5);
334                 remove_pid(pidfilename);
335                 execvp(g_argv[0], g_argv);
336         } else {
337                 logger(LOG_NOTICE, _("Not restarting."));
338                 exit(1);
339         }
340 }
341
342 static RETSIGTYPE sighup_handler(int a)
343 {
344         logger(LOG_NOTICE, _("Got HUP signal"));
345         sighup = true;
346 }
347
348 static RETSIGTYPE sigint_handler(int a)
349 {
350         if(saved_debug_level != -1) {
351                 logger(LOG_NOTICE, _("Reverting to old debug level (%d)"),
352                         saved_debug_level);
353                 debug_level = saved_debug_level;
354                 saved_debug_level = -1;
355         } else {
356                 logger(LOG_NOTICE,
357                         _("Temporarily setting debug level to 5.  Kill me with SIGINT again to go back to level %d."),
358                         debug_level);
359                 saved_debug_level = debug_level;
360                 debug_level = 5;
361         }
362 }
363
364 static RETSIGTYPE sigalrm_handler(int a)
365 {
366         logger(LOG_NOTICE, _("Got ALRM signal"));
367         sigalrm = true;
368 }
369
370 static RETSIGTYPE sigusr1_handler(int a)
371 {
372         dump_connections();
373 }
374
375 static RETSIGTYPE sigusr2_handler(int a)
376 {
377         dump_device_stats();
378         dump_nodes();
379         dump_edges();
380         dump_subnets();
381 }
382
383 static RETSIGTYPE sigwinch_handler(int a)
384 {
385         do_purge = true;
386 }
387
388 static RETSIGTYPE unexpected_signal_handler(int a)
389 {
390         logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
391         cp_trace();
392 }
393
394 static RETSIGTYPE ignore_signal_handler(int a)
395 {
396         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
397 }
398
399 static struct {
400         int signal;
401         void (*handler)(int);
402 } sighandlers[] = {
403         {SIGHUP, sighup_handler},
404         {SIGTERM, sigterm_handler},
405         {SIGQUIT, sigquit_handler},
406         {SIGSEGV, fatal_signal_handler},
407         {SIGBUS, fatal_signal_handler},
408         {SIGILL, fatal_signal_handler},
409         {SIGPIPE, ignore_signal_handler},
410         {SIGINT, sigint_handler},
411         {SIGUSR1, sigusr1_handler},
412         {SIGUSR2, sigusr2_handler},
413         {SIGCHLD, ignore_signal_handler},
414         {SIGALRM, sigalrm_handler},
415         {SIGWINCH, sigwinch_handler},
416         {0, NULL}
417 };
418
419 void setup_signals(void)
420 {
421         int i;
422         struct sigaction act;
423
424         sigemptyset(&emptysigset);
425         act.sa_handler = NULL;
426         act.sa_mask = emptysigset;
427         act.sa_flags = 0;
428
429         /* Set a default signal handler for every signal, errors will be
430            ignored. */
431         for(i = 0; i < NSIG; i++) {
432                 if(!do_detach)
433                         act.sa_handler = SIG_DFL;
434                 else
435                         act.sa_handler = unexpected_signal_handler;
436                 sigaction(i, &act, NULL);
437         }
438
439         /* If we didn't detach, allow coredumps */
440         if(!do_detach)
441                 sighandlers[3].handler = SIG_DFL;
442
443         /* Then, for each known signal that we want to catch, assign a
444            handler to the signal, with error checking this time. */
445         for(i = 0; sighandlers[i].signal; i++) {
446                 act.sa_handler = sighandlers[i].handler;
447                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
448                         fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
449                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
450                                         strerror(errno));
451         }
452 }