]> 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-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                 DWORD lasterror = GetLastError();
102                 logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
103                 if(lasterror != ERROR_SERVICE_EXISTS)
104                         return false;
105         }
106
107         if(service) {
108                 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
109                 logger(LOG_INFO, "%s service installed", identname);
110         } else {
111                 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
112         }
113
114         if(!StartService(service, 0, NULL))
115                 logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
116         else
117                 logger(LOG_INFO, "%s service started", identname);
118
119         return true;
120 }
121
122 bool remove_service(void) {
123         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
124         if(!manager) {
125                 logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
126                 return false;
127         }
128
129         service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
130
131         if(!service) {
132                 logger(LOG_ERR, "Could not open %s service: %s", identname, winerror(GetLastError()));
133                 return false;
134         }
135
136         if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
137                 logger(LOG_ERR, "Could not stop %s service: %s", identname, winerror(GetLastError()));
138         else
139                 logger(LOG_INFO, "%s service stopped", identname);
140
141         if(!DeleteService(service)) {
142                 logger(LOG_ERR, "Could not remove %s service: %s", identname, winerror(GetLastError()));
143                 return false;
144         }
145
146         logger(LOG_INFO, "%s service removed", identname);
147
148         return true;
149 }
150
151 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
152         switch(request) {
153                 case SERVICE_CONTROL_INTERROGATE:
154                         SetServiceStatus(statushandle, &status);
155                         return NO_ERROR;
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         event_loopexit(NULL);
168         status.dwWaitHint = 30000; 
169         status.dwCurrentState = SERVICE_STOP_PENDING; 
170         SetServiceStatus(statushandle, &status);
171         return NO_ERROR;
172 }
173
174 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
175         int err = 1;
176         extern int main2(int argc, char **argv);
177
178
179         status.dwServiceType = SERVICE_WIN32; 
180         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
181         status.dwWin32ExitCode = 0; 
182         status.dwServiceSpecificExitCode = 0; 
183         status.dwCheckPoint = 0; 
184
185         statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
186
187         if (!statushandle) {
188                 logger(LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
189                 err = 1;
190         } else {
191                 status.dwWaitHint = 30000; 
192                 status.dwCurrentState = SERVICE_START_PENDING; 
193                 SetServiceStatus(statushandle, &status);
194
195                 status.dwWaitHint = 0; 
196                 status.dwCurrentState = SERVICE_RUNNING;
197                 SetServiceStatus(statushandle, &status);
198
199                 err = main2(argc, argv);
200
201                 status.dwWaitHint = 0;
202                 status.dwCurrentState = SERVICE_STOPPED; 
203                 //status.dwWin32ExitCode = err; 
204                 SetServiceStatus(statushandle, &status);
205         }
206
207         return;
208 }
209
210 bool init_service(void) {
211         SERVICE_TABLE_ENTRY services[] = {
212                 {identname, run_service},
213                 {NULL, NULL}
214         };
215
216         if(!StartServiceCtrlDispatcher(services)) {
217                 if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
218                         return false;
219                 }
220                 else
221                         logger(LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
222         }
223
224         return true;
225 }
226 #endif
227
228 /*
229   Detach from current terminal
230 */
231 bool detach(void) {
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 #ifndef HAVE_MINGW
268         len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
269 #else
270         len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
271 #endif
272         if(len < 0)
273                 return false;
274
275         scriptname[len - 1] = '\0';
276
277 #ifndef HAVE_TUNEMU
278         /* First check if there is a script */
279
280         if(access(scriptname + 1, F_OK)) {
281                 free(scriptname);
282                 return true;
283         }
284 #endif
285
286         ifdebug(STATUS) logger(LOG_INFO, "Executing script %s", name);
287
288 #ifdef HAVE_PUTENV
289         /* Set environment */
290         
291         for(i = 0; envp[i]; i++)
292                 putenv(envp[i]);
293 #endif
294
295         scriptname[len - 1] = '\"';
296         status = system(scriptname);
297
298         free(scriptname);
299
300         /* Unset environment */
301
302         for(i = 0; envp[i]; i++) {
303                 char *e = strchr(envp[i], '=');
304                 if(e) {
305                         p = alloca(e - envp[i] + 1);
306                         strncpy(p, envp[i], e - envp[i]);
307                         p[e - envp[i]] = '\0';
308                         putenv(p);
309                 }
310         }
311
312 #ifdef WEXITSTATUS
313         if(status != -1) {
314                 if(WIFEXITED(status)) { /* Child exited by itself */
315                         if(WEXITSTATUS(status)) {
316                                 logger(LOG_ERR, "Script %s exited with non-zero status %d",
317                                            name, WEXITSTATUS(status));
318                                 return false;
319                         }
320                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
321                         logger(LOG_ERR, "Script %s was killed by signal %d (%s)",
322                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
323                         return false;
324                 } else {                        /* Something strange happened */
325                         logger(LOG_ERR, "Script %s terminated abnormally", name);
326                         return false;
327                 }
328         } else {
329                 logger(LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
330                 return false;
331         }
332 #endif
333 #endif
334         return true;
335 }
336
337
338 /*
339   Signal handlers.
340 */
341
342 #ifndef HAVE_MINGW
343 static RETSIGTYPE fatal_signal_square(int a) {
344         logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
345                    strsignal(a));
346         exit(1);
347 }
348
349 static RETSIGTYPE fatal_signal_handler(int a) {
350         struct sigaction act;
351         logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
352
353         if(do_detach) {
354                 logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
355
356                 act.sa_handler = fatal_signal_square;
357                 act.sa_mask = emptysigset;
358                 act.sa_flags = 0;
359                 sigaction(SIGSEGV, &act, NULL);
360
361                 close_network_connections();
362                 sleep(5);
363                 exit_control();
364                 execvp(g_argv[0], g_argv);
365         } else {
366                 logger(LOG_NOTICE, "Not restarting.");
367                 exit(1);
368         }
369 }
370
371 static RETSIGTYPE unexpected_signal_handler(int a) {
372         logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
373 }
374
375 static RETSIGTYPE ignore_signal_handler(int a) {
376         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
377 }
378
379 static struct {
380         int signal;
381         void (*handler)(int);
382 } sighandlers[] = {
383         {SIGSEGV, fatal_signal_handler},
384         {SIGBUS, fatal_signal_handler},
385         {SIGILL, fatal_signal_handler},
386         {SIGPIPE, ignore_signal_handler},
387         {SIGCHLD, ignore_signal_handler},
388         {0, NULL}
389 };
390 #endif
391
392 void setup_signals(void) {
393 #ifndef HAVE_MINGW
394         int i;
395         struct sigaction act;
396
397         sigemptyset(&emptysigset);
398         act.sa_handler = NULL;
399         act.sa_mask = emptysigset;
400         act.sa_flags = 0;
401
402         /* Set a default signal handler for every signal, errors will be
403            ignored. */
404         for(i = 1; i < NSIG; i++) {
405                 if(!do_detach)
406                         act.sa_handler = SIG_DFL;
407                 else
408                         act.sa_handler = unexpected_signal_handler;
409                 sigaction(i, &act, NULL);
410         }
411
412         /* If we didn't detach, allow coredumps */
413         if(!do_detach)
414                 sighandlers[0].handler = SIG_DFL;
415
416         /* Then, for each known signal that we want to catch, assign a
417            handler to the signal, with error checking this time. */
418         for(i = 0; sighandlers[i].signal; i++) {
419                 act.sa_handler = sighandlers[i].handler;
420                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
421                         fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
422                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
423                                         strerror(errno));
424         }
425 #endif
426 }