]> git.meshlink.io Git - meshlink/blob - src/process.c
Merge branch 'master' into 1.1
[meshlink] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2007 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 "control.h"
28 #include "device.h"
29 #include "edge.h"
30 #include "logger.h"
31 #include "node.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 **g_argv;
43 extern bool use_logfile;
44
45 sigset_t emptysigset;
46
47 static void memory_full(int size) {
48         logger(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size);
49         cp_trace();
50         exit(1);
51 }
52
53 /* Some functions the less gifted operating systems might lack... */
54
55 #ifdef HAVE_MINGW
56 extern char *identname;
57 extern char *program_name;
58 extern char **g_argv;
59
60 static SC_HANDLE manager = NULL;
61 static SC_HANDLE service = NULL;
62 static SERVICE_STATUS status = {0};
63 static SERVICE_STATUS_HANDLE statushandle = 0;
64
65 bool install_service(void) {
66         char command[4096] = "\"";
67         char **argp;
68         bool space;
69         SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
70
71         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
72         if(!manager) {
73                 logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
74                 return false;
75         }
76
77         if(!strchr(program_name, '\\')) {
78                 GetCurrentDirectory(sizeof command - 1, command + 1);
79                 strncat(command, "\\", sizeof command - strlen(command));
80         }
81
82         strncat(command, program_name, sizeof command - strlen(command));
83
84         strncat(command, "\"", sizeof command - strlen(command));
85
86         for(argp = g_argv + 1; *argp; argp++) {
87                 space = strchr(*argp, ' ');
88                 strncat(command, " ", sizeof command - strlen(command));
89                 
90                 if(space)
91                         strncat(command, "\"", sizeof command - strlen(command));
92                 
93                 strncat(command, *argp, sizeof command - strlen(command));
94
95                 if(space)
96                         strncat(command, "\"", sizeof command - strlen(command));
97         }
98
99         service = CreateService(manager, identname, identname,
100                         SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
101                         command, NULL, NULL, NULL, NULL, NULL);
102         
103         if(!service) {
104                 logger(LOG_ERR, _("Could not create %s service: %s"), identname, winerror(GetLastError()));
105                 return false;
106         }
107
108         ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
109
110         logger(LOG_INFO, _("%s service installed"), identname);
111
112         if(!StartService(service, 0, NULL))
113                 logger(LOG_WARNING, _("Could not start %s service: %s"), identname, winerror(GetLastError()));
114         else
115                 logger(LOG_INFO, _("%s service started"), identname);
116
117         return true;
118 }
119
120 bool remove_service(void) {
121         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
122         if(!manager) {
123                 logger(LOG_ERR, _("Could not open service manager: %s"), winerror(GetLastError()));
124                 return false;
125         }
126
127         service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
128
129         if(!service) {
130                 logger(LOG_ERR, _("Could not open %s service: %s"), identname, winerror(GetLastError()));
131                 return false;
132         }
133
134         if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
135                 logger(LOG_ERR, _("Could not stop %s service: %s"), identname, winerror(GetLastError()));
136         else
137                 logger(LOG_INFO, _("%s service stopped"), identname);
138
139         if(!DeleteService(service)) {
140                 logger(LOG_ERR, _("Could not remove %s service: %s"), identname, winerror(GetLastError()));
141                 return false;
142         }
143
144         logger(LOG_INFO, _("%s service removed"), identname);
145
146         return true;
147 }
148
149 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
150         switch(request) {
151                 case SERVICE_CONTROL_INTERROGATE:
152                         SetServiceStatus(statushandle, &status);
153                         return NO_ERROR;
154                 case SERVICE_CONTROL_STOP:
155                         logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_STOP");
156                         break;
157                 case SERVICE_CONTROL_SHUTDOWN:
158                         logger(LOG_NOTICE, _("Got %s request"), "SERVICE_CONTROL_SHUTDOWN");
159                         break;
160                 default:
161                         logger(LOG_WARNING, _("Got unexpected request %d"), request);
162                         return ERROR_CALL_NOT_IMPLEMENTED;
163         }
164
165         event_loopexit(NULL);
166         status.dwWaitHint = 30000; 
167         status.dwCurrentState = SERVICE_STOP_PENDING; 
168         SetServiceStatus(statushandle, &status);
169         return NO_ERROR;
170 }
171
172 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
173         int err = 1;
174         extern int main2(int argc, char **argv);
175
176
177         status.dwServiceType = SERVICE_WIN32; 
178         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
179         status.dwWin32ExitCode = 0; 
180         status.dwServiceSpecificExitCode = 0; 
181         status.dwCheckPoint = 0; 
182
183         statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
184
185         if (!statushandle) {
186                 logger(LOG_ERR, _("System call `%s' failed: %s"), "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
187                 err = 1;
188         } else {
189                 status.dwWaitHint = 30000; 
190                 status.dwCurrentState = SERVICE_START_PENDING; 
191                 SetServiceStatus(statushandle, &status);
192
193                 status.dwWaitHint = 0; 
194                 status.dwCurrentState = SERVICE_RUNNING;
195                 SetServiceStatus(statushandle, &status);
196
197                 err = main2(argc, argv);
198
199                 status.dwWaitHint = 0;
200                 status.dwCurrentState = SERVICE_STOPPED; 
201                 //status.dwWin32ExitCode = err; 
202                 SetServiceStatus(statushandle, &status);
203         }
204
205         return;
206 }
207
208 bool init_service(void) {
209         SERVICE_TABLE_ENTRY services[] = {
210                 {identname, run_service},
211                 {NULL, NULL}
212         };
213
214         if(!StartServiceCtrlDispatcher(services)) {
215                 if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
216                         return false;
217                 }
218                 else
219                         logger(LOG_ERR, _("System call `%s' failed: %s"), "StartServiceCtrlDispatcher", winerror(GetLastError()));
220         }
221
222         return true;
223 }
224 #endif
225
226 /*
227   Detach from current terminal
228 */
229 bool detach(void) {
230         cp();
231
232         setup_signals();
233
234 #ifndef HAVE_MINGW
235         closelogger();
236 #endif
237
238         if(do_detach) {
239 #ifndef HAVE_MINGW
240                 if(daemon(0, 0)) {
241                         fprintf(stderr, _("Couldn't detach from terminal: %s"),
242                                         strerror(errno));
243                         return false;
244                 }
245 #else
246                 if(!statushandle)
247                         exit(install_service());
248 #endif
249         }
250
251         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
252
253         logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
254                            VERSION, __DATE__, __TIME__, debug_level);
255
256         xalloc_fail_func = memory_full;
257
258         return true;
259 }
260
261 bool execute_script(const char *name, char **envp) {
262 #ifdef HAVE_SYSTEM
263         int status, len;
264         char *scriptname, *p;
265         int i;
266
267         cp();
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                         p = alloca(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 fatal_signal_square(int a) {
346         logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
347                    strsignal(a));
348         cp_trace();
349         exit(1);
350 }
351
352 static RETSIGTYPE fatal_signal_handler(int a) {
353         struct sigaction act;
354         logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
355         cp_trace();
356
357         if(do_detach) {
358                 logger(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
359
360                 act.sa_handler = fatal_signal_square;
361                 act.sa_mask = emptysigset;
362                 act.sa_flags = 0;
363                 sigaction(SIGSEGV, &act, NULL);
364
365                 close_network_connections();
366                 sleep(5);
367                 exit_control();
368                 execvp(g_argv[0], g_argv);
369         } else {
370                 logger(LOG_NOTICE, _("Not restarting."));
371                 exit(1);
372         }
373 }
374
375 static RETSIGTYPE unexpected_signal_handler(int a) {
376         logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
377         cp_trace();
378 }
379
380 static RETSIGTYPE ignore_signal_handler(int a) {
381         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
382 }
383
384 static struct {
385         int signal;
386         void (*handler)(int);
387 } sighandlers[] = {
388         {SIGSEGV, fatal_signal_handler},
389         {SIGBUS, fatal_signal_handler},
390         {SIGILL, fatal_signal_handler},
391         {SIGPIPE, ignore_signal_handler},
392         {SIGCHLD, ignore_signal_handler},
393         {0, NULL}
394 };
395 #endif
396
397 void setup_signals(void) {
398 #ifndef HAVE_MINGW
399         int i;
400         struct sigaction act;
401
402         sigemptyset(&emptysigset);
403         act.sa_handler = NULL;
404         act.sa_mask = emptysigset;
405         act.sa_flags = 0;
406
407         /* Set a default signal handler for every signal, errors will be
408            ignored. */
409         for(i = 1; i < NSIG; i++) {
410                 if(!do_detach)
411                         act.sa_handler = SIG_DFL;
412                 else
413                         act.sa_handler = unexpected_signal_handler;
414                 sigaction(i, &act, NULL);
415         }
416
417         /* If we didn't detach, allow coredumps */
418         if(!do_detach)
419                 sighandlers[0].handler = SIG_DFL;
420
421         /* Then, for each known signal that we want to catch, assign a
422            handler to the signal, with error checking this time. */
423         for(i = 0; sighandlers[i].signal; i++) {
424                 act.sa_handler = sighandlers[i].handler;
425                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
426                         fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
427                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
428                                         strerror(errno));
429         }
430 #endif
431 }