process: Port child process spawning to the Windows platform
[strongswan.git] / src / libstrongswan / utils / process.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "process.h"
17
18 #include <utils/debug.h>
19
20 #include <fcntl.h>
21
22 typedef struct private_process_t private_process_t;
23
24 /**
25 * Ends of a pipe()
26 */
27 enum {
28 PIPE_READ = 0,
29 PIPE_WRITE = 1,
30 PIPE_ENDS,
31 };
32
33 #ifndef WIN32
34
35 #include <unistd.h>
36 #include <errno.h>
37 #include <sys/wait.h>
38
39 /**
40 * Private data of an process_t object.
41 */
42 struct private_process_t {
43
44 /**
45 * Public process_t interface.
46 */
47 process_t public;
48
49 /**
50 * child stdin pipe
51 */
52 int in[PIPE_ENDS];
53
54 /**
55 * child stdout pipe
56 */
57 int out[PIPE_ENDS];
58
59 /**
60 * child stderr pipe
61 */
62 int err[PIPE_ENDS];
63
64 /**
65 * child process
66 */
67 int pid;
68 };
69
70 /**
71 * Close a file descriptor if it is not -1
72 */
73 static void close_if(int *fd)
74 {
75 if (*fd != -1)
76 {
77 close(*fd);
78 *fd = -1;
79 }
80 }
81
82 /**
83 * Destroy a process structure, close all pipes
84 */
85 static void process_destroy(private_process_t *this)
86 {
87 close_if(&this->in[PIPE_READ]);
88 close_if(&this->in[PIPE_WRITE]);
89 close_if(&this->out[PIPE_READ]);
90 close_if(&this->out[PIPE_WRITE]);
91 close_if(&this->err[PIPE_READ]);
92 close_if(&this->err[PIPE_WRITE]);
93 free(this);
94 }
95
96 METHOD(process_t, wait_, bool,
97 private_process_t *this, int *code)
98 {
99 int status, ret;
100
101 ret = waitpid(this->pid, &status, 0);
102 process_destroy(this);
103 if (ret == -1)
104 {
105 return FALSE;
106 }
107 if (!WIFEXITED(status))
108 {
109 return FALSE;
110 }
111 if (code)
112 {
113 *code = WEXITSTATUS(status);
114 }
115 return TRUE;
116 }
117
118 /**
119 * See header
120 */
121 process_t* process_start(char *const argv[], char *const envp[],
122 int *in, int *out, int *err, bool close_all)
123 {
124 private_process_t *this;
125 char *empty[] = { NULL };
126
127 INIT(this,
128 .public = {
129 .wait = _wait_,
130 },
131 .in = { -1, -1 },
132 .out = { -1, -1 },
133 .err = { -1, -1 },
134 );
135
136 if (in && pipe(this->in) != 0)
137 {
138 DBG1(DBG_LIB, "creating stdin pipe failed: %s", strerror(errno));
139 process_destroy(this);
140 return NULL;
141 }
142 if (out && pipe(this->out) != 0)
143 {
144 DBG1(DBG_LIB, "creating stdout pipe failed: %s", strerror(errno));
145 process_destroy(this);
146 return NULL;
147 }
148 if (err && pipe(this->err) != 0)
149 {
150 DBG1(DBG_LIB, "creating stderr pipe failed: %s", strerror(errno));
151 process_destroy(this);
152 return NULL;
153 }
154
155 this->pid = fork();
156 switch (this->pid)
157 {
158 case -1:
159 DBG1(DBG_LIB, "forking process failed: %s", strerror(errno));
160 process_destroy(this);
161 return NULL;
162 case 0:
163 /* child */
164 close_if(&this->in[PIPE_WRITE]);
165 close_if(&this->out[PIPE_READ]);
166 close_if(&this->err[PIPE_READ]);
167 if (this->in[PIPE_READ] != -1)
168 {
169 if (dup2(this->in[PIPE_READ], 0) == -1)
170 {
171 raise(SIGKILL);
172 }
173 }
174 if (this->out[PIPE_WRITE] != -1)
175 {
176 if (dup2(this->out[PIPE_WRITE], 1) == -1)
177 {
178 raise(SIGKILL);
179 }
180 }
181 if (this->err[PIPE_WRITE] != -1)
182 {
183 if (dup2(this->err[PIPE_WRITE], 2) == -1)
184 {
185 raise(SIGKILL);
186 }
187 }
188 if (close_all)
189 {
190 closefrom(3);
191 }
192 if (execve(argv[0], argv, envp ?: empty) == -1)
193 {
194 raise(SIGKILL);
195 }
196 /* not reached */
197 default:
198 /* parent */
199 close_if(&this->in[PIPE_READ]);
200 close_if(&this->out[PIPE_WRITE]);
201 close_if(&this->err[PIPE_WRITE]);
202 if (in)
203 {
204 *in = this->in[PIPE_WRITE];
205 this->in[PIPE_WRITE] = -1;
206 }
207 if (out)
208 {
209 *out = this->out[PIPE_READ];
210 this->out[PIPE_READ] = -1;
211 }
212 if (err)
213 {
214 *err = this->err[PIPE_READ];
215 this->err[PIPE_READ] = -1;
216 }
217 return &this->public;
218 }
219 }
220
221 #else /* WIN32 */
222
223 /**
224 * Private data of an process_t object.
225 */
226 struct private_process_t {
227
228 /**
229 * Public process_t interface.
230 */
231 process_t public;
232
233 /**
234 * child stdin pipe
235 */
236 HANDLE in[PIPE_ENDS];
237
238 /**
239 * child stdout pipe
240 */
241 HANDLE out[PIPE_ENDS];
242
243 /**
244 * child stderr pipe
245 */
246 HANDLE err[PIPE_ENDS];
247
248 /**
249 * child process information
250 */
251 PROCESS_INFORMATION pi;
252 };
253
254 /**
255 * Clean up state associated to child process
256 */
257 static void process_destroy(private_process_t *this)
258 {
259 if (this->in[PIPE_READ])
260 {
261 CloseHandle(this->in[PIPE_READ]);
262 }
263 if (this->in[PIPE_WRITE])
264 {
265 CloseHandle(this->in[PIPE_WRITE]);
266 }
267 if (this->out[PIPE_READ])
268 {
269 CloseHandle(this->out[PIPE_READ]);
270 }
271 if (this->out[PIPE_WRITE])
272 {
273 CloseHandle(this->out[PIPE_WRITE]);
274 }
275 if (this->err[PIPE_READ])
276 {
277 CloseHandle(this->err[PIPE_READ]);
278 }
279 if (this->err[PIPE_WRITE])
280 {
281 CloseHandle(this->err[PIPE_WRITE]);
282 }
283 if (this->pi.hProcess)
284 {
285 CloseHandle(this->pi.hProcess);
286 CloseHandle(this->pi.hThread);
287 }
288 free(this);
289 }
290
291 METHOD(process_t, wait_, bool,
292 private_process_t *this, int *code)
293 {
294 DWORD ec;
295
296 if (WaitForSingleObject(this->pi.hProcess, INFINITE) != WAIT_OBJECT_0)
297 {
298 DBG1(DBG_LIB, "waiting for child process failed: 0x%08x",
299 GetLastError());
300 process_destroy(this);
301 return FALSE;
302 }
303 if (code)
304 {
305 if (!GetExitCodeProcess(this->pi.hProcess, &ec))
306 {
307 DBG1(DBG_LIB, "getting child process exit code failed: 0x%08x",
308 GetLastError());
309 process_destroy(this);
310 return FALSE;
311 }
312 *code = ec;
313 }
314 process_destroy(this);
315 return TRUE;
316 }
317
318 /**
319 * Append a command line argument to buf, optionally quoted
320 */
321 static void append_arg(char *buf, u_int len, char *arg, char *quote)
322 {
323 char *space = "";
324 int current;
325
326 current = strlen(buf);
327 if (current)
328 {
329 space = " ";
330 }
331 snprintf(buf + current, len - current, "%s%s%s%s", space, quote, arg, quote);
332 }
333
334 /**
335 * Append a null-terminate env string to buf
336 */
337 static void append_env(char *buf, u_int len, char *env)
338 {
339 char *pos = buf;
340 int current;
341
342 while (TRUE)
343 {
344 pos += strlen(pos);
345 if (!pos[1])
346 {
347 if (pos == buf)
348 {
349 current = 0;
350 }
351 else
352 {
353 current = pos - buf + 1;
354 }
355 snprintf(buf + current, len - current, "%s", env);
356 break;
357 }
358 pos++;
359 }
360 }
361
362 /**
363 * See header
364 */
365 process_t* process_start(char *const argv[], char *const envp[],
366 int *in, int *out, int *err, bool close_all)
367 {
368 private_process_t *this;
369 char arg[32768], env[32768];
370 SECURITY_ATTRIBUTES sa = {
371 .nLength = sizeof(SECURITY_ATTRIBUTES),
372 .bInheritHandle = TRUE,
373 };
374 STARTUPINFO sui = {
375 .cb = sizeof(STARTUPINFO),
376 };
377 int i;
378
379 memset(arg, 0, sizeof(arg));
380 memset(env, 0, sizeof(env));
381
382 for (i = 0; argv[i]; i++)
383 {
384 if (!strchr(argv[i], ' '))
385 { /* no spaces, fine for appending */
386 append_arg(arg, sizeof(arg) - 1, argv[i], "");
387 }
388 else if (argv[i][0] == '"' &&
389 argv[i][strlen(argv[i]) - 1] == '"' &&
390 strchr(argv[i] + 1, '"') == argv[i] + strlen(argv[i]) - 1)
391 { /* already properly quoted */
392 append_arg(arg, sizeof(arg) - 1, argv[i], "");
393 }
394 else if (strchr(argv[i], ' ') && !strchr(argv[i], '"'))
395 { /* spaces, but no quotes; append quoted */
396 append_arg(arg, sizeof(arg) - 1, argv[i], "\"");
397 }
398 else
399 {
400 DBG1(DBG_LIB, "invalid command line argument: %s", argv[i]);
401 return NULL;
402 }
403 }
404 if (envp)
405 {
406 for (i = 0; envp[i]; i++)
407 {
408 append_env(env, sizeof(env) - 1, envp[i]);
409 }
410 }
411
412 INIT(this,
413 .public = {
414 .wait = _wait_,
415 },
416 );
417
418 if (in)
419 {
420 sui.dwFlags = STARTF_USESTDHANDLES;
421 if (!CreatePipe(&this->in[PIPE_READ], &this->in[PIPE_WRITE], &sa, 0))
422 {
423 process_destroy(this);
424 return NULL;
425 }
426 if (!SetHandleInformation(this->in[PIPE_WRITE], HANDLE_FLAG_INHERIT, 0))
427 {
428 process_destroy(this);
429 return NULL;
430 }
431 sui.hStdInput = this->in[PIPE_READ];
432 *in = _open_osfhandle((uintptr_t)this->in[PIPE_WRITE], 0);
433 if (*in == -1)
434 {
435 process_destroy(this);
436 return NULL;
437 }
438 }
439 if (out)
440 {
441 sui.dwFlags = STARTF_USESTDHANDLES;
442 if (!CreatePipe(&this->out[PIPE_READ], &this->out[PIPE_WRITE], &sa, 0))
443 {
444 process_destroy(this);
445 return NULL;
446 }
447 if (!SetHandleInformation(this->out[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
448 {
449 process_destroy(this);
450 return NULL;
451 }
452 sui.hStdOutput = this->out[PIPE_WRITE];
453 *out = _open_osfhandle((uintptr_t)this->out[PIPE_READ], 0);
454 if (*out == -1)
455 {
456 process_destroy(this);
457 return NULL;
458 }
459 }
460 if (err)
461 {
462 sui.dwFlags = STARTF_USESTDHANDLES;
463 if (!CreatePipe(&this->err[PIPE_READ], &this->err[PIPE_WRITE], &sa, 0))
464 {
465 process_destroy(this);
466 return NULL;
467 }
468 if (!SetHandleInformation(this->err[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
469 {
470 process_destroy(this);
471 return NULL;
472 }
473 sui.hStdError = this->err[PIPE_WRITE];
474 *err = _open_osfhandle((uintptr_t)this->err[PIPE_READ], 0);
475 if (*err == -1)
476 {
477 process_destroy(this);
478 return NULL;
479 }
480 }
481
482 if (!CreateProcess(argv[0], arg, NULL, NULL, TRUE,
483 NORMAL_PRIORITY_CLASS, env, NULL, &sui, &this->pi))
484 {
485 DBG1(DBG_LIB, "creating process '%s' failed: 0x%08x",
486 argv[0], GetLastError());
487 process_destroy(this);
488 return NULL;
489 }
490
491 /* close child process end of pipes */
492 if (this->in[PIPE_READ])
493 {
494 CloseHandle(this->in[PIPE_READ]);
495 this->in[PIPE_READ] = NULL;
496 }
497 if (this->out[PIPE_WRITE])
498 {
499 CloseHandle(this->out[PIPE_WRITE]);
500 this->out[PIPE_WRITE] = NULL;
501 }
502 if (this->err[PIPE_WRITE])
503 {
504 CloseHandle(this->err[PIPE_WRITE]);
505 this->err[PIPE_WRITE] = NULL;
506 }
507 /* our side gets closed over the osf_handle closed by caller */
508 this->in[PIPE_WRITE] = NULL;
509 this->out[PIPE_READ] = NULL;
510 this->err[PIPE_READ] = NULL;
511 return &this->public;
512 }
513
514 #endif /* WIN32 */