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