Merge branch 'master' into 1.1
[tinc] / src / process.c
1 /*
2     process.c -- process management functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2011 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 "net.h"
30 #include "node.h"
31 #include "process.h"
32 #include "subnet.h"
33 #include "utils.h"
34 #include "xalloc.h"
35
36 /* If zero, don't detach from the terminal. */
37 bool do_detach = true;
38 bool sigalrm = false;
39
40 extern char *identname;
41 extern char **g_argv;
42 extern bool use_logfile;
43
44 /* Some functions the less gifted operating systems might lack... */
45
46 #ifdef HAVE_MINGW
47 extern char *identname;
48 extern char *program_name;
49 extern char **g_argv;
50
51 static SC_HANDLE manager = NULL;
52 static SC_HANDLE service = NULL;
53 static SERVICE_STATUS status = {0};
54 static SERVICE_STATUS_HANDLE statushandle = 0;
55
56 static bool install_service(void) {
57         char command[4096] = "\"";
58         char **argp;
59         bool space;
60         SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
61
62         manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
63         if(!manager) {
64                 logger(DEBUG_ALWAYS, LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
65                 return false;
66         }
67
68         if(!strchr(program_name, '\\')) {
69                 GetCurrentDirectory(sizeof command - 1, command + 1);
70                 strncat(command, "\\", sizeof command - strlen(command));
71         }
72
73         strncat(command, program_name, sizeof command - strlen(command));
74
75         strncat(command, "\"", sizeof command - strlen(command));
76
77         for(argp = g_argv + 1; *argp; argp++) {
78                 space = strchr(*argp, ' ');
79                 strncat(command, " ", sizeof command - strlen(command));
80                 
81                 if(space)
82                         strncat(command, "\"", sizeof command - strlen(command));
83                 
84                 strncat(command, *argp, sizeof command - strlen(command));
85
86                 if(space)
87                         strncat(command, "\"", sizeof command - strlen(command));
88         }
89
90         service = CreateService(manager, identname, identname,
91                         SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
92                         command, NULL, NULL, NULL, NULL, NULL);
93         
94         if(!service) {
95                 DWORD lasterror = GetLastError();
96                 logger(DEBUG_ALWAYS, LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
97                 if(lasterror != ERROR_SERVICE_EXISTS)
98                         return false;
99         }
100
101         if(service) {
102                 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
103                 logger(DEBUG_ALWAYS, LOG_INFO, "%s service installed", identname);
104         } else {
105                 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
106         }
107
108         if(!StartService(service, 0, NULL))
109                 logger(DEBUG_ALWAYS, LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
110         else
111                 logger(DEBUG_ALWAYS, LOG_INFO, "%s service started", identname);
112
113         return true;
114 }
115
116 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
117         switch(request) {
118                 case SERVICE_CONTROL_INTERROGATE:
119                         SetServiceStatus(statushandle, &status);
120                         return NO_ERROR;
121                 case SERVICE_CONTROL_STOP:
122                         logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
123                         break;
124                 case SERVICE_CONTROL_SHUTDOWN:
125                         logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
126                         break;
127                 default:
128                         logger(DEBUG_ALWAYS, LOG_WARNING, "Got unexpected request %d", request);
129                         return ERROR_CALL_NOT_IMPLEMENTED;
130         }
131
132         event_loopexit(NULL);
133         status.dwWaitHint = 30000; 
134         status.dwCurrentState = SERVICE_STOP_PENDING; 
135         SetServiceStatus(statushandle, &status);
136         return NO_ERROR;
137 }
138
139 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
140         int err = 1;
141         extern int main2(int argc, char **argv);
142
143
144         status.dwServiceType = SERVICE_WIN32; 
145         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
146         status.dwWin32ExitCode = 0; 
147         status.dwServiceSpecificExitCode = 0; 
148         status.dwCheckPoint = 0; 
149
150         statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
151
152         if (!statushandle) {
153                 logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
154                 err = 1;
155         } else {
156                 status.dwWaitHint = 30000; 
157                 status.dwCurrentState = SERVICE_START_PENDING; 
158                 SetServiceStatus(statushandle, &status);
159
160                 status.dwWaitHint = 0; 
161                 status.dwCurrentState = SERVICE_RUNNING;
162                 SetServiceStatus(statushandle, &status);
163
164                 err = main2(argc, argv);
165
166                 status.dwWaitHint = 0;
167                 status.dwCurrentState = SERVICE_STOPPED; 
168                 //status.dwWin32ExitCode = err; 
169                 SetServiceStatus(statushandle, &status);
170         }
171
172         return;
173 }
174
175 bool init_service(void) {
176         SERVICE_TABLE_ENTRY services[] = {
177                 {identname, run_service},
178                 {NULL, NULL}
179         };
180
181         if(!StartServiceCtrlDispatcher(services)) {
182                 if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
183                         return false;
184                 }
185                 else
186                         logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
187         }
188
189         return true;
190 }
191 #endif
192
193 /*
194   Detach from current terminal
195 */
196 bool detach(void) {
197 #ifndef HAVE_MINGW
198         signal(SIGPIPE, SIG_IGN);
199         signal(SIGUSR1, SIG_IGN);
200         signal(SIGUSR2, SIG_IGN);
201         signal(SIGWINCH, SIG_IGN);
202
203         closelogger();
204 #endif
205
206         if(do_detach) {
207 #ifndef HAVE_MINGW
208                 if(daemon(0, 0)) {
209                         fprintf(stderr, "Couldn't detach from terminal: %s",
210                                         strerror(errno));
211                         return false;
212                 }
213 #else
214                 if(!statushandle)
215                         exit(!install_service());
216 #endif
217         }
218
219         openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
220
221         logger(DEBUG_ALWAYS, LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
222                            VERSION, __DATE__, __TIME__, debug_level);
223
224         return true;
225 }
226
227 bool execute_script(const char *name, char **envp) {
228 #ifdef HAVE_SYSTEM
229         int status, len;
230         char *scriptname;
231         int i;
232         char *interpreter = NULL;
233
234 #ifndef HAVE_MINGW
235         len = xasprintf(&scriptname, "\"%s" SLASH "%s\"", confbase, name);
236 #else
237         len = xasprintf(&scriptname, "\"%s" SLASH "%s.bat\"", confbase, name);
238 #endif
239         if(len < 0)
240                 return false;
241
242         scriptname[len - 1] = '\0';
243
244 #ifndef HAVE_TUNEMU
245         /* First check if there is a script */
246
247         if(access(scriptname + 1, F_OK)) {
248                 free(scriptname);
249                 return true;
250         }
251 #endif
252
253         // Custom scripts interpreter
254         if(get_config_string(lookup_config(config_tree, "ScriptsInterpreter"), &interpreter)) {
255                 // Force custom scripts interpreter allowing execution of scripts on android without execution flag (such as on /sdcard)
256                 free(scriptname);
257                 len = xasprintf(&scriptname, "%s \"%s/%s\"", interpreter, confbase, name);
258                 free(interpreter);
259                 if(len < 0)
260                         return false;
261         }
262
263         logger(DEBUG_STATUS, LOG_INFO, "Executing script %s", name);
264
265
266 #ifdef HAVE_PUTENV
267         /* Set environment */
268         
269         for(i = 0; envp[i]; i++)
270                 putenv(envp[i]);
271 #endif
272
273         scriptname[len - 1] = '\"';
274         status = system(scriptname);
275
276         free(scriptname);
277
278         /* Unset environment */
279
280         for(i = 0; envp[i]; i++) {
281                 char *e = strchr(envp[i], '=');
282                 if(e) {
283                         char p[e - envp[i] + 1];
284                         strncpy(p, envp[i], e - envp[i]);
285                         p[e - envp[i]] = '\0';
286                         putenv(p);
287                 }
288         }
289
290 #ifdef WEXITSTATUS
291         if(status != -1) {
292                 if(WIFEXITED(status)) { /* Child exited by itself */
293                         if(WEXITSTATUS(status)) {
294                                 logger(DEBUG_ALWAYS, LOG_ERR, "Script %s exited with non-zero status %d",
295                                            name, WEXITSTATUS(status));
296                                 return false;
297                         }
298                 } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
299                         logger(DEBUG_ALWAYS, LOG_ERR, "Script %s was killed by signal %d (%s)",
300                                    name, WTERMSIG(status), strsignal(WTERMSIG(status)));
301                         return false;
302                 } else {                        /* Something strange happened */
303                         logger(DEBUG_ALWAYS, LOG_ERR, "Script %s terminated abnormally", name);
304                         return false;
305                 }
306         } else {
307                 logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
308                 return false;
309         }
310 #endif
311 #endif
312         return true;
313 }