]> git.meshlink.io Git - meshlink/blob - src/process.c
Fix pointer arithmetic when creating and verifying message authentication codes.
[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         struct stat s;
265         char *scriptname, *p;
266         int i;
267
268         cp();
269
270 #ifndef HAVE_MINGW
271         len = asprintf(&scriptname, "\"%s/%s\"", confbase, name);
272 #else
273         len = asprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
274 #endif
275         if(len < 0)
276                 return false;
277
278         scriptname[len - 1] = '\0';
279
280         /* First check if there is a script */
281
282         if(stat(scriptname + 1, &s)) {
283                 free(scriptname);
284                 return true;
285         }
286
287         ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name);
288
289 #ifdef HAVE_PUTENV
290         /* Set environment */
291         
292         for(i = 0; envp[i]; i++)
293                 putenv(envp[i]);
294 #endif
295
296         scriptname[len - 1] = '\"';
297         status = system(scriptname);
298
299         free(scriptname);
300
301         /* Unset environment */
302
303         for(i = 0; envp[i]; i++) {
304                 char *e = strchr(envp[i], '=');
305                 if(e) {
306                         p = alloca(e - envp[i] + 1);
307                         strncpy(p, envp[i], e - envp[i]);
308                         p[e - envp[i]] = '\0';
309                         putenv(p);
310                 }
311         }
312
313 #ifdef WEXITSTATUS
314         if(status != -1) {
315                 if(WIFEXITED(status)) { /* Child exited by itself */
316                         if(WEXITSTATUS(status)) {
317                                 logger(LOG_ERR, _("Script %s exited with non-zero status %d"),
318                                            name, WEXITSTATUS(status));
319                                 return false;
320                         }
321                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
322                         logger(LOG_ERR, _("Script %s was killed by signal %d (%s)"),
323                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
324                         return false;
325                 } else {                        /* Something strange happened */
326                         logger(LOG_ERR, _("Script %s terminated abnormally"), name);
327                         return false;
328                 }
329         } else {
330                 logger(LOG_ERR, _("System call `%s' failed: %s"), "system", strerror(errno));
331                 return false;
332         }
333 #endif
334 #endif
335         return true;
336 }
337
338
339 /*
340   Signal handlers.
341 */
342
343 #ifndef HAVE_MINGW
344 static RETSIGTYPE fatal_signal_square(int a) {
345         logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
346                    strsignal(a));
347         cp_trace();
348         exit(1);
349 }
350
351 static RETSIGTYPE fatal_signal_handler(int a) {
352         struct sigaction act;
353         logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
354         cp_trace();
355
356         if(do_detach) {
357                 logger(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
358
359                 act.sa_handler = fatal_signal_square;
360                 act.sa_mask = emptysigset;
361                 act.sa_flags = 0;
362                 sigaction(SIGSEGV, &act, NULL);
363
364                 close_network_connections();
365                 sleep(5);
366                 exit_control();
367                 execvp(g_argv[0], g_argv);
368         } else {
369                 logger(LOG_NOTICE, _("Not restarting."));
370                 exit(1);
371         }
372 }
373
374 static RETSIGTYPE unexpected_signal_handler(int a) {
375         logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
376         cp_trace();
377 }
378
379 static RETSIGTYPE ignore_signal_handler(int a) {
380         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
381 }
382
383 static struct {
384         int signal;
385         void (*handler)(int);
386 } sighandlers[] = {
387         {SIGSEGV, fatal_signal_handler},
388         {SIGBUS, fatal_signal_handler},
389         {SIGILL, fatal_signal_handler},
390         {SIGPIPE, ignore_signal_handler},
391         {SIGCHLD, ignore_signal_handler},
392         {0, NULL}
393 };
394 #endif
395
396 void setup_signals(void) {
397 #ifndef HAVE_MINGW
398         int i;
399         struct sigaction act;
400
401         sigemptyset(&emptysigset);
402         act.sa_handler = NULL;
403         act.sa_mask = emptysigset;
404         act.sa_flags = 0;
405
406         /* Set a default signal handler for every signal, errors will be
407            ignored. */
408         for(i = 0; i < NSIG; i++) {
409                 if(!do_detach)
410                         act.sa_handler = SIG_DFL;
411                 else
412                         act.sa_handler = unexpected_signal_handler;
413                 sigaction(i, &act, NULL);
414         }
415
416         /* If we didn't detach, allow coredumps */
417         if(!do_detach)
418                 sighandlers[0].handler = SIG_DFL;
419
420         /* Then, for each known signal that we want to catch, assign a
421            handler to the signal, with error checking this time. */
422         for(i = 0; sighandlers[i].signal; i++) {
423                 act.sa_handler = sighandlers[i].handler;
424                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
425                         fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
426                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
427                                         strerror(errno));
428         }
429 #endif
430 }