Remove pidfile in favour of control socket.
[tinc] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2006 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_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         cp();
228
229         setup_signals();
230
231 #ifndef HAVE_MINGW
232         closelogger();
233 #endif
234
235         if(do_detach) {
236 #ifndef HAVE_MINGW
237                 if(daemon(0, 0)) {
238                         fprintf(stderr, _("Couldn't detach from terminal: %s"),
239                                         strerror(errno));
240                         return false;
241                 }
242 #else
243                 if(!statushandle)
244                         exit(install_service());
245 #endif
246         }
247
248         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
249
250         logger(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
251                            VERSION, __DATE__, __TIME__, debug_level);
252
253         xalloc_fail_func = memory_full;
254
255         return true;
256 }
257
258 bool execute_script(const char *name, char **envp) {
259 #ifdef HAVE_SYSTEM
260         int status, len;
261         struct stat s;
262         char *scriptname, *p;
263         int i;
264
265         cp();
266
267 #ifndef HAVE_MINGW
268         len = asprintf(&scriptname, "\"%s/%s\"", confbase, name);
269 #else
270         len = asprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
271 #endif
272         if(len < 0)
273                 return false;
274
275         scriptname[len - 1] = '\0';
276
277         /* First check if there is a script */
278
279         if(stat(scriptname + 1, &s)) {
280                 free(scriptname);
281                 return true;
282         }
283
284         ifdebug(STATUS) logger(LOG_INFO, _("Executing script %s"), name);
285
286 #ifdef HAVE_PUTENV
287         /* Set environment */
288         
289         for(i = 0; envp[i]; i++)
290                 putenv(envp[i]);
291 #endif
292
293         scriptname[len - 1] = '\"';
294         status = system(scriptname);
295
296         free(scriptname);
297
298         /* Unset environment */
299
300         for(i = 0; envp[i]; i++) {
301                 char *e = strchr(envp[i], '=');
302                 if(e) {
303                         p = alloca(e - envp[i] + 1);
304                         strncpy(p, envp[i], e - envp[i]);
305                         p[e - envp[i]] = '\0';
306                         putenv(p);
307                 }
308         }
309
310 #ifdef WEXITSTATUS
311         if(status != -1) {
312                 if(WIFEXITED(status)) { /* Child exited by itself */
313                         if(WEXITSTATUS(status)) {
314                                 logger(LOG_ERR, _("Script %s exited with non-zero status %d"),
315                                            name, WEXITSTATUS(status));
316                                 return false;
317                         }
318                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
319                         logger(LOG_ERR, _("Script %s was killed by signal %d (%s)"),
320                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
321                         return false;
322                 } else {                        /* Something strange happened */
323                         logger(LOG_ERR, _("Script %s terminated abnormally"), name);
324                         return false;
325                 }
326         } else {
327                 logger(LOG_ERR, _("System call `%s' failed: %s"), "system", strerror(errno));
328                 return false;
329         }
330 #endif
331 #endif
332         return true;
333 }
334
335
336 /*
337   Signal handlers.
338 */
339
340 #ifndef HAVE_MINGW
341 static RETSIGTYPE fatal_signal_square(int a) {
342         logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a,
343                    strsignal(a));
344         cp_trace();
345         exit(1);
346 }
347
348 static RETSIGTYPE fatal_signal_handler(int a) {
349         struct sigaction act;
350         logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
351         cp_trace();
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         cp_trace();
374 }
375
376 static RETSIGTYPE ignore_signal_handler(int a) {
377         ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
378 }
379
380 static struct {
381         int signal;
382         void (*handler)(int);
383 } sighandlers[] = {
384         {SIGSEGV, fatal_signal_handler},
385         {SIGBUS, fatal_signal_handler},
386         {SIGILL, fatal_signal_handler},
387         {SIGPIPE, ignore_signal_handler},
388         {SIGCHLD, ignore_signal_handler},
389         {0, NULL}
390 };
391 #endif
392
393 void setup_signals(void) {
394 #ifndef HAVE_MINGW
395         int i;
396         struct sigaction act;
397
398         sigemptyset(&emptysigset);
399         act.sa_handler = NULL;
400         act.sa_mask = emptysigset;
401         act.sa_flags = 0;
402
403         /* Set a default signal handler for every signal, errors will be
404            ignored. */
405         for(i = 0; i < NSIG; i++) {
406                 if(!do_detach)
407                         act.sa_handler = SIG_DFL;
408                 else
409                         act.sa_handler = unexpected_signal_handler;
410                 sigaction(i, &act, NULL);
411         }
412
413         /* If we didn't detach, allow coredumps */
414         if(!do_detach)
415                 sighandlers[0].handler = SIG_DFL;
416
417         /* Then, for each known signal that we want to catch, assign a
418            handler to the signal, with error checking this time. */
419         for(i = 0; sighandlers[i].signal; i++) {
420                 act.sa_handler = sighandlers[i].handler;
421                 if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
422                         fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
423                                         sighandlers[i].signal, strsignal(sighandlers[i].signal),
424                                         strerror(errno));
425         }
426 #endif
427 }