Use libevent to handle all non-fatal signals.
[tinc] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2006 Guus Sliepen <guus@tinc-vpn.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$
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 sigalrm = false;
40
41 extern char *identname;
42 extern char *pidfilename;
43 extern char **g_argv;
44 extern bool use_logfile;
45 extern volatile bool running;
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 #ifdef HAVE_MINGW
61 extern char *identname;
62 extern char *program_name;
63 extern char **g_argv;
64
65 static SC_HANDLE manager = NULL;
66 static SC_HANDLE service = NULL;
67 static SERVICE_STATUS status = {0};
68 static SERVICE_STATUS_HANDLE statushandle = 0;
69
70 bool install_service(void) {
71         char command[4096] = "\"";
72         char **argp;
73         bool space;
74         SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
75
76         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
77         if(!manager) {
78                 logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
79                 return false;
80         }
81
82         if(!strchr(program_name, '\\')) {
83                 GetCurrentDirectory(sizeof command - 1, command + 1);
84                 strncat(command, "\\", sizeof command - strlen(command));
85         }
86
87         strncat(command, program_name, sizeof command - strlen(command));
88
89         strncat(command, "\"", sizeof command - strlen(command));
90
91         for(argp = g_argv + 1; *argp; argp++) {
92                 space = strchr(*argp, ' ');
93                 strncat(command, " ", sizeof command - strlen(command));
94                 
95                 if(space)
96                         strncat(command, "\"", sizeof command - strlen(command));
97                 
98                 strncat(command, *argp, sizeof command - strlen(command));
99
100                 if(space)
101                         strncat(command, "\"", sizeof command - strlen(command));
102         }
103
104         service = CreateService(manager, identname, identname,
105                         SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
106                         command, NULL, NULL, NULL, NULL, NULL);
107         
108         if(!service) {
109                 logger(LOG_ERR, _("Could not create %s service: %s"), identname, winerror(GetLastError()));
110                 return false;
111         }
112
113         ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
114
115         logger(LOG_INFO, _("%s service installed"), identname);
116
117         if(!StartService(service, 0, NULL))
118                 logger(LOG_WARNING, _("Could not start %s service: %s"), identname, winerror(GetLastError()));
119         else
120                 logger(LOG_INFO, _("%s service started"), identname);
121
122         return true;
123 }
124
125 bool remove_service(void) {
126         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
127         if(!manager) {
128                 logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
129                 return false;
130         }
131
132         service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
133
134         if(!service) {
135                 logger(LOG_ERR, _("Could not open %s service: %s"), identname, winerror(GetLastError()));
136                 return false;
137         }
138
139         if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
140                 logger(LOG_ERR, _("Could not stop %s service: %s"), identname, winerror(GetLastError()));
141         else
142                 logger(LOG_INFO, _("%s service stopped"), identname);
143
144         if(!DeleteService(service)) {
145                 logger(LOG_ERR, _("Could not remove %s service: %s"), identname, winerror(GetLastError()));
146                 return false;
147         }
148
149         logger(LOG_INFO, _("%s service removed"), identname);
150
151         return true;
152 }
153
154 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
155         switch(request) {
156                 case SERVICE_CONTROL_STOP:
157                         logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_STOP");
158                         break;
159                 case SERVICE_CONTROL_SHUTDOWN:
160                         logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_SHUTDOWN");
161                         break;
162                 default:
163                         logger(LOG_WARNING, _("Got unexpected request %d"), request);
164                         return ERROR_CALL_NOT_IMPLEMENTED;
165         }
166
167         if(running) {
168                 running = false;
169                 status.dwWaitHint = 30000; 
170                 status.dwCurrentState = SERVICE_STOP_PENDING; 
171                 SetServiceStatus(statushandle, &status);
172                 return NO_ERROR;
173         } else {
174                 status.dwWaitHint = 0; 
175                 status.dwCurrentState = SERVICE_STOPPED; 
176                 SetServiceStatus(statushandle, &status);
177                 exit(1);
178         }
179
180 }
181
182 VOID WINAPI run_service(DWORD argc, LPTSTR* argv)
183 {
184         int err = 1;
185         extern int main2(int argc, char **argv);
186
187
188         status.dwServiceType = SERVICE_WIN32; 
189         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
190         status.dwWin32ExitCode = 0; 
191         status.dwServiceSpecificExitCode = 0; 
192         status.dwCheckPoint = 0; 
193
194         statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
195
196         if (!statushandle) {
197                 logger(LOG_ERR, _("System call `%s' failed: %s"), "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
198                 err = 1;
199         } else {
200                 status.dwWaitHint = 30000; 
201                 status.dwCurrentState = SERVICE_START_PENDING; 
202                 SetServiceStatus(statushandle, &status);
203
204                 status.dwWaitHint = 0; 
205                 status.dwCurrentState = SERVICE_RUNNING;
206                 SetServiceStatus(statushandle, &status);
207
208                 err = main2(argc, argv);
209
210                 status.dwWaitHint = 0;
211                 status.dwCurrentState = SERVICE_STOPPED; 
212                 //status.dwWin32ExitCode = err; 
213                 SetServiceStatus(statushandle, &status);
214         }
215
216         return;
217 }
218
219 bool init_service(void) {
220         SERVICE_TABLE_ENTRY services[] = {
221                 {identname, run_service},
222                 {NULL, NULL}
223         };
224
225         if(!StartServiceCtrlDispatcher(services)) {
226                 if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
227                         return false;
228                 }
229                 else
230                         logger(LOG_ERR, _("System call `%s' failed: %s"), "StartServiceCtrlDispatcher", winerror(GetLastError()));
231         }
232
233         return true;
234 }
235 #endif
236
237 #ifndef HAVE_MINGW
238 /*
239   check for an existing tinc for this net, and write pid to pidfile
240 */
241 static bool write_pidfile(void)
242 {
243         pid_t pid;
244
245         cp();
246
247         pid = check_pid(pidfilename);
248
249         if(pid) {
250                 if(netname)
251                         fprintf(stderr, _("A tincd is already running for net `%s' with pid %ld.\n"),
252                                         netname, (long)pid);
253                 else
254                         fprintf(stderr, _("A tincd is already running with pid %ld.\n"), (long)pid);
255                 return false;
256         }
257
258         /* if it's locked, write-protected, or whatever */
259         if(!write_pid(pidfilename)) {
260                 fprintf(stderr, _("Could write pid file %s: %s\n"), pidfilename, strerror(errno));
261                 return false;
262         }
263
264         return true;
265 }
266 #endif
267
268 /*
269   kill older tincd for this net
270 */
271 bool kill_other(int signal)
272 {
273 #ifndef HAVE_MINGW
274         pid_t pid;
275
276         cp();
277
278         pid = read_pid(pidfilename);
279
280         if(!pid) {
281                 if(netname)
282                         fprintf(stderr, _("No other tincd is running for net `%s'.\n"),
283                                         netname);
284                 else
285                         fprintf(stderr, _("No other tincd is running.\n"));
286                 return false;
287         }
288
289         errno = 0;                                      /* No error, sometimes errno is only changed on error */
290
291         /* ESRCH is returned when no process with that pid is found */
292         if(kill(pid, signal) && errno == ESRCH) {
293                 if(netname)
294                         fprintf(stderr, _("The tincd for net `%s' is no longer running. "),
295                                         netname);
296                 else
297                         fprintf(stderr, _("The tincd is no longer running. "));
298
299                 fprintf(stderr, _("Removing stale lock file.\n"));
300                 remove_pid(pidfilename);
301         }
302
303         return true;
304 #else
305         return remove_service();
306 #endif
307 }
308
309 /*
310   Detach from current terminal, write pidfile, kill parent
311 */
312 bool detach(void)
313 {
314         cp();
315
316         setup_signals();
317
318         /* First check if we can open a fresh new pidfile */
319
320 #ifndef HAVE_MINGW
321         if(!write_pidfile())
322                 return false;
323
324         /* If we succeeded in doing that, detach */
325
326         closelogger();
327 #endif
328
329         if(do_detach) {
330 #ifndef HAVE_MINGW
331                 if(daemon(0, 0)) {
332                         fprintf(stderr, _("Couldn't detach from terminal: %s"),
333                                         strerror(errno));
334                         return false;
335                 }
336
337                 /* Now UPDATE the pid in the pidfile, because we changed it... */
338
339                 if(!write_pid(pidfilename)) {
340                         fprintf(stderr, _("Could not write pid file %s: %s\n"), pidfilename, strerror(errno));
341                         return false;
342                 }
343 #else
344                 if(!statushandle)
345                         exit(install_service());
346 #endif
347         }
348
349         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
350
351         logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
352                            VERSION, __DATE__, __TIME__, debug_level);
353
354         xalloc_fail_func = memory_full;
355
356         return true;
357 }
358
359 bool execute_script(const char *name, char **envp)
360 {
361 #ifdef HAVE_SYSTEM
362         int status, len;
363         struct stat s;
364         char *scriptname, *p;
365         int i;
366
367         cp();
368
369 #ifndef HAVE_MINGW
370         len = asprintf(&scriptname, "\"%s/%s\"", confbase, name);
371 #else
372         len = asprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
373 #endif
374         if(len < 0)
375                 return false;
376
377         scriptname[len - 1] = '\0';
378
379         /* First check if there is a script */
380
381         if(stat(scriptname + 1, &s)) {
382                 free(scriptname);
383                 return true;
384         }
385
386         ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name);
387
388 #ifdef HAVE_PUTENV
389         /* Set environment */
390         
391         for(i = 0; envp[i]; i++)
392                 putenv(envp[i]);
393 #endif
394
395         scriptname[len - 1] = '\"';
396         status = system(scriptname);
397
398         free(scriptname);
399
400         /* Unset environment */
401
402         for(i = 0; envp[i]; i++) {
403                 char *e = strchr(envp[i], '=');
404                 if(e) {
405                         p = alloca(e - envp[i] + 1);
406                         strncpy(p, envp[i], e - envp[i]);
407                         p[e - envp[i]] = '\0';
408                         putenv(p);
409                 }
410         }
411
412 #ifdef WEXITSTATUS
413         if(status != -1) {
414                 if(WIFEXITED(status)) { /* Child exited by itself */
415                         if(WEXITSTATUS(status)) {
416                                 logger(LOG_ERR, _("Script %s exited with non-zero status %d"),
417                                            name, WEXITSTATUS(status));
418                                 return false;
419                         }
420                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
421                         logger(LOG_ERR, _("Script %s was killed by signal %d (%s)"),
422                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
423                         return false;
424                 } else {                        /* Something strange happened */
425                         logger(LOG_ERR, _("Script %s terminated abnormally"), name);
426                         return false;
427                 }
428         } else {
429                 logger(LOG_ERR, _("System call `%s' failed: %s"), "system", strerror(errno));
430                 return false;
431         }
432 #endif
433 #endif
434         return true;
435 }
436
437
438 /*
439   Signal handlers.
440 */
441
442 #ifndef HAVE_MINGW
443 static RETSIGTYPE fatal_signal_square(int a)
444 {
445         logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
446                    strsignal(a));
447         cp_trace();
448         exit(1);
449 }
450
451 static RETSIGTYPE fatal_signal_handler(int a)
452 {
453         struct sigaction act;
454         logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
455         cp_trace();
456
457         if(do_detach) {
458                 logger(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
459
460                 act.sa_handler = fatal_signal_square;
461                 act.sa_mask = emptysigset;
462                 act.sa_flags = 0;
463                 sigaction(SIGSEGV, &act, NULL);
464
465                 close_network_connections();
466                 sleep(5);
467                 remove_pid(pidfilename);
468                 execvp(g_argv[0], g_argv);
469         } else {
470                 logger(LOG_NOTICE, _("Not restarting."));
471                 exit(1);
472         }
473 }
474
475 static RETSIGTYPE unexpected_signal_handler(int a)
476 {
477         logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
478         cp_trace();
479 }
480
481 static RETSIGTYPE ignore_signal_handler(int a)
482 {
483         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
484 }
485
486 static struct {
487         int signal;
488         void (*handler)(int);
489 } sighandlers[] = {
490         {SIGSEGV, fatal_signal_handler},
491         {SIGBUS, fatal_signal_handler},
492         {SIGILL, fatal_signal_handler},
493         {SIGPIPE, ignore_signal_handler},
494         {SIGCHLD, ignore_signal_handler},
495         {0, NULL}
496 };
497 #endif
498
499 void setup_signals(void)
500 {
501 #ifndef HAVE_MINGW
502         int i;
503         struct sigaction act;
504
505         sigemptyset(&emptysigset);
506         act.sa_handler = NULL;
507         act.sa_mask = emptysigset;
508         act.sa_flags = 0;
509
510         /* Set a default signal handler for every signal, errors will be
511            ignored. */
512         for(i = 0; i < NSIG; i++) {
513                 if(!do_detach)
514                         act.sa_handler = SIG_DFL;
515                 else
516                         act.sa_handler = unexpected_signal_handler;
517                 sigaction(i, &act, NULL);
518         }
519
520         /* If we didn't detach, allow coredumps */
521         if(!do_detach)
522                 sighandlers[0].handler = SIG_DFL;
523
524         /* Then, for each known signal that we want to catch, assign a
525            handler to the signal, with error checking this time. */
526         for(i = 0; sighandlers[i].signal; i++) {
527                 act.sa_handler = sighandlers[i].handler;
528                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
529                         fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
530                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
531                                         strerror(errno));
532         }
533 #endif
534 }