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