Merge branch 'master' into 1.1
[tinc] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2009 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 along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "conf.h"
24 #include "connection.h"
25 #include "control.h"
26 #include "device.h"
27 #include "edge.h"
28 #include "logger.h"
29 #include "node.h"
30 #include "process.h"
31 #include "subnet.h"
32 #include "utils.h"
33 #include "xalloc.h"
34
35 /* If zero, don't detach from the terminal. */
36 bool do_detach = true;
37 bool sigalrm = false;
38
39 extern char *identname;
40 extern char **g_argv;
41 extern bool use_logfile;
42
43 sigset_t emptysigset;
44
45 static void memory_full(int size) {
46         logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
47         exit(1);
48 }
49
50 /* Some functions the less gifted operating systems might lack... */
51
52 #ifdef HAVE_MINGW
53 extern char *identname;
54 extern char *program_name;
55 extern char **g_argv;
56
57 static SC_HANDLE manager = NULL;
58 static SC_HANDLE service = NULL;
59 static SERVICE_STATUS status = {0};
60 static SERVICE_STATUS_HANDLE statushandle = 0;
61
62 bool install_service(void) {
63         char command[4096] = "\"";
64         char **argp;
65         bool space;
66         SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
67
68         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
69         if(!manager) {
70                 logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
71                 return false;
72         }
73
74         if(!strchr(program_name, '\\')) {
75                 GetCurrentDirectory(sizeof command - 1, command + 1);
76                 strncat(command, "\\", sizeof command - strlen(command));
77         }
78
79         strncat(command, program_name, sizeof command - strlen(command));
80
81         strncat(command, "\"", sizeof command - strlen(command));
82
83         for(argp = g_argv + 1; *argp; argp++) {
84                 space = strchr(*argp, ' ');
85                 strncat(command, " ", sizeof command - strlen(command));
86                 
87                 if(space)
88                         strncat(command, "\"", sizeof command - strlen(command));
89                 
90                 strncat(command, *argp, sizeof command - strlen(command));
91
92                 if(space)
93                         strncat(command, "\"", sizeof command - strlen(command));
94         }
95
96         service = CreateService(manager, identname, identname,
97                         SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
98                         command, NULL, NULL, NULL, NULL, NULL);
99         
100         if(!service) {
101                 logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(GetLastError()));
102                 return false;
103         }
104
105         ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
106
107         logger(LOG_INFO, "%s service installed", identname);
108
109         if(!StartService(service, 0, NULL))
110                 logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
111         else
112                 logger(LOG_INFO, "%s service started", identname);
113
114         return true;
115 }
116
117 bool remove_service(void) {
118         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
119         if(!manager) {
120                 logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
121                 return false;
122         }
123
124         service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
125
126         if(!service) {
127                 logger(LOG_ERR, "Could not open %s service: %s", identname, winerror(GetLastError()));
128                 return false;
129         }
130
131         if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
132                 logger(LOG_ERR, "Could not stop %s service: %s", identname, winerror(GetLastError()));
133         else
134                 logger(LOG_INFO, "%s service stopped", identname);
135
136         if(!DeleteService(service)) {
137                 logger(LOG_ERR, "Could not remove %s service: %s", identname, winerror(GetLastError()));
138                 return false;
139         }
140
141         logger(LOG_INFO, "%s service removed", identname);
142
143         return true;
144 }
145
146 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
147         switch(request) {
148                 case SERVICE_CONTROL_INTERROGATE:
149                         SetServiceStatus(statushandle, &status);
150                         return NO_ERROR;
151                 case SERVICE_CONTROL_STOP:
152                         logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
153                         break;
154                 case SERVICE_CONTROL_SHUTDOWN:
155                         logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
156                         break;
157                 default:
158                         logger(LOG_WARNING, "Got unexpected request %d", request);
159                         return ERROR_CALL_NOT_IMPLEMENTED;
160         }
161
162         event_loopexit(NULL);
163         status.dwWaitHint = 30000; 
164         status.dwCurrentState = SERVICE_STOP_PENDING; 
165         SetServiceStatus(statushandle, &status);
166         return NO_ERROR;
167 }
168
169 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
170         int err = 1;
171         extern int main2(int argc, char **argv);
172
173
174         status.dwServiceType = SERVICE_WIN32; 
175         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
176         status.dwWin32ExitCode = 0; 
177         status.dwServiceSpecificExitCode = 0; 
178         status.dwCheckPoint = 0; 
179
180         statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
181
182         if (!statushandle) {
183                 logger(LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
184                 err = 1;
185         } else {
186                 status.dwWaitHint = 30000; 
187                 status.dwCurrentState = SERVICE_START_PENDING; 
188                 SetServiceStatus(statushandle, &status);
189
190                 status.dwWaitHint = 0; 
191                 status.dwCurrentState = SERVICE_RUNNING;
192                 SetServiceStatus(statushandle, &status);
193
194                 err = main2(argc, argv);
195
196                 status.dwWaitHint = 0;
197                 status.dwCurrentState = SERVICE_STOPPED; 
198                 //status.dwWin32ExitCode = err; 
199                 SetServiceStatus(statushandle, &status);
200         }
201
202         return;
203 }
204
205 bool init_service(void) {
206         SERVICE_TABLE_ENTRY services[] = {
207                 {identname, run_service},
208                 {NULL, NULL}
209         };
210
211         if(!StartServiceCtrlDispatcher(services)) {
212                 if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
213                         return false;
214                 }
215                 else
216                         logger(LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
217         }
218
219         return true;
220 }
221 #endif
222
223 /*
224   Detach from current terminal
225 */
226 bool detach(void) {
227         setup_signals();
228
229 #ifndef HAVE_MINGW
230         closelogger();
231 #endif
232
233         if(do_detach) {
234 #ifndef HAVE_MINGW
235                 if(daemon(0, 0)) {
236                         fprintf(stderr, "Couldn't detach from terminal: %s",
237                                         strerror(errno));
238                         return false;
239                 }
240 #else
241                 if(!statushandle)
242                         exit(install_service());
243 #endif
244         }
245
246         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
247
248         logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
249                            VERSION, __DATE__, __TIME__, debug_level);
250
251         xalloc_fail_func = memory_full;
252
253         return true;
254 }
255
256 bool execute_script(const char *name, char **envp) {
257 #ifdef HAVE_SYSTEM
258         int status, len;
259         char *scriptname, *p;
260         int i;
261
262 #ifndef HAVE_MINGW
263         len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
264 #else
265         len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
266 #endif
267         if(len < 0)
268                 return false;
269
270         scriptname[len - 1] = '\0';
271
272 #ifndef HAVE_TUNEMU
273         /* First check if there is a script */
274
275         if(access(scriptname + 1, F_OK)) {
276                 free(scriptname);
277                 return true;
278         }
279 #endif
280
281         ifdebug(STATUS) logger(LOG_INFO, "Executing script %s", name);
282
283 #ifdef HAVE_PUTENV
284         /* Set environment */
285         
286         for(i = 0; envp[i]; i++)
287                 putenv(envp[i]);
288 #endif
289
290         scriptname[len - 1] = '\"';
291         status = system(scriptname);
292
293         free(scriptname);
294
295         /* Unset environment */
296
297         for(i = 0; envp[i]; i++) {
298                 char *e = strchr(envp[i], '=');
299                 if(e) {
300                         p = alloca(e - envp[i] + 1);
301                         strncpy(p, envp[i], e - envp[i]);
302                         p[e - envp[i]] = '\0';
303                         putenv(p);
304                 }
305         }
306
307 #ifdef WEXITSTATUS
308         if(status != -1) {
309                 if(WIFEXITED(status)) { /* Child exited by itself */
310                         if(WEXITSTATUS(status)) {
311                                 logger(LOG_ERR, "Script %s exited with non-zero status %d",
312                                            name, WEXITSTATUS(status));
313                                 return false;
314                         }
315                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
316                         logger(LOG_ERR, "Script %s was killed by signal %d (%s)",
317                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
318                         return false;
319                 } else {                        /* Something strange happened */
320                         logger(LOG_ERR, "Script %s terminated abnormally", name);
321                         return false;
322                 }
323         } else {
324                 logger(LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
325                 return false;
326         }
327 #endif
328 #endif
329         return true;
330 }
331
332
333 /*
334   Signal handlers.
335 */
336
337 #ifndef HAVE_MINGW
338 static RETSIGTYPE fatal_signal_square(int a) {
339         logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
340                    strsignal(a));
341         exit(1);
342 }
343
344 static RETSIGTYPE fatal_signal_handler(int a) {
345         struct sigaction act;
346         logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
347
348         if(do_detach) {
349                 logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
350
351                 act.sa_handler = fatal_signal_square;
352                 act.sa_mask = emptysigset;
353                 act.sa_flags = 0;
354                 sigaction(SIGSEGV, &act, NULL);
355
356                 close_network_connections();
357                 sleep(5);
358                 exit_control();
359                 execvp(g_argv[0], g_argv);
360         } else {
361                 logger(LOG_NOTICE, "Not restarting.");
362                 exit(1);
363         }
364 }
365
366 static RETSIGTYPE unexpected_signal_handler(int a) {
367         logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
368 }
369
370 static RETSIGTYPE ignore_signal_handler(int a) {
371         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
372 }
373
374 static struct {
375         int signal;
376         void (*handler)(int);
377 } sighandlers[] = {
378         {SIGSEGV, fatal_signal_handler},
379         {SIGBUS, fatal_signal_handler},
380         {SIGILL, fatal_signal_handler},
381         {SIGPIPE, ignore_signal_handler},
382         {SIGCHLD, ignore_signal_handler},
383         {0, NULL}
384 };
385 #endif
386
387 void setup_signals(void) {
388 #ifndef HAVE_MINGW
389         int i;
390         struct sigaction act;
391
392         sigemptyset(&emptysigset);
393         act.sa_handler = NULL;
394         act.sa_mask = emptysigset;
395         act.sa_flags = 0;
396
397         /* Set a default signal handler for every signal, errors will be
398            ignored. */
399         for(i = 1; i < NSIG; i++) {
400                 if(!do_detach)
401                         act.sa_handler = SIG_DFL;
402                 else
403                         act.sa_handler = unexpected_signal_handler;
404                 sigaction(i, &act, NULL);
405         }
406
407         /* If we didn't detach, allow coredumps */
408         if(!do_detach)
409                 sighandlers[0].handler = SIG_DFL;
410
411         /* Then, for each known signal that we want to catch, assign a
412            handler to the signal, with error checking this time. */
413         for(i = 0; sighandlers[i].signal; i++) {
414                 act.sa_handler = sighandlers[i].handler;
415                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
416                         fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
417                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
418                                         strerror(errno));
419         }
420 #endif
421 }