process: Include missing <signal.h> for raise(3)
[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 /* vasprintf() */
17 #define _GNU_SOURCE
18 #include "process.h"
19
20 #include <library.h>
21 #include <utils/debug.h>
22
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26
27 typedef struct private_process_t private_process_t;
28
29 /**
30 * Ends of a pipe()
31 */
32 enum {
33 PIPE_READ = 0,
34 PIPE_WRITE = 1,
35 PIPE_ENDS,
36 };
37
38 #ifndef WIN32
39
40 #include <unistd.h>
41 #include <errno.h>
42 #include <sys/wait.h>
43 #include <signal.h>
44
45 /**
46 * Private data of an process_t object.
47 */
48 struct private_process_t {
49
50 /**
51 * Public process_t interface.
52 */
53 process_t public;
54
55 /**
56 * child stdin pipe
57 */
58 int in[PIPE_ENDS];
59
60 /**
61 * child stdout pipe
62 */
63 int out[PIPE_ENDS];
64
65 /**
66 * child stderr pipe
67 */
68 int err[PIPE_ENDS];
69
70 /**
71 * child process
72 */
73 int pid;
74 };
75
76 /**
77 * Close a file descriptor if it is not -1
78 */
79 static void close_if(int *fd)
80 {
81 if (*fd != -1)
82 {
83 close(*fd);
84 *fd = -1;
85 }
86 }
87
88 /**
89 * Destroy a process structure, close all pipes
90 */
91 static void process_destroy(private_process_t *this)
92 {
93 close_if(&this->in[PIPE_READ]);
94 close_if(&this->in[PIPE_WRITE]);
95 close_if(&this->out[PIPE_READ]);
96 close_if(&this->out[PIPE_WRITE]);
97 close_if(&this->err[PIPE_READ]);
98 close_if(&this->err[PIPE_WRITE]);
99 free(this);
100 }
101
102 METHOD(process_t, wait_, bool,
103 private_process_t *this, int *code)
104 {
105 int status, ret;
106
107 ret = waitpid(this->pid, &status, 0);
108 process_destroy(this);
109 if (ret == -1)
110 {
111 return FALSE;
112 }
113 if (!WIFEXITED(status))
114 {
115 return FALSE;
116 }
117 if (code)
118 {
119 *code = WEXITSTATUS(status);
120 }
121 return TRUE;
122 }
123
124 /**
125 * See header
126 */
127 process_t* process_start(char *const argv[], char *const envp[],
128 int *in, int *out, int *err, bool close_all)
129 {
130 private_process_t *this;
131 char *empty[] = { NULL };
132
133 INIT(this,
134 .public = {
135 .wait = _wait_,
136 },
137 .in = { -1, -1 },
138 .out = { -1, -1 },
139 .err = { -1, -1 },
140 );
141
142 if (in && pipe(this->in) != 0)
143 {
144 DBG1(DBG_LIB, "creating stdin pipe failed: %s", strerror(errno));
145 process_destroy(this);
146 return NULL;
147 }
148 if (out && pipe(this->out) != 0)
149 {
150 DBG1(DBG_LIB, "creating stdout pipe failed: %s", strerror(errno));
151 process_destroy(this);
152 return NULL;
153 }
154 if (err && pipe(this->err) != 0)
155 {
156 DBG1(DBG_LIB, "creating stderr pipe failed: %s", strerror(errno));
157 process_destroy(this);
158 return NULL;
159 }
160
161 this->pid = fork();
162 switch (this->pid)
163 {
164 case -1:
165 DBG1(DBG_LIB, "forking process failed: %s", strerror(errno));
166 process_destroy(this);
167 return NULL;
168 case 0:
169 /* child */
170 close_if(&this->in[PIPE_WRITE]);
171 close_if(&this->out[PIPE_READ]);
172 close_if(&this->err[PIPE_READ]);
173 if (this->in[PIPE_READ] != -1)
174 {
175 if (dup2(this->in[PIPE_READ], 0) == -1)
176 {
177 raise(SIGKILL);
178 }
179 }
180 if (this->out[PIPE_WRITE] != -1)
181 {
182 if (dup2(this->out[PIPE_WRITE], 1) == -1)
183 {
184 raise(SIGKILL);
185 }
186 }
187 if (this->err[PIPE_WRITE] != -1)
188 {
189 if (dup2(this->err[PIPE_WRITE], 2) == -1)
190 {
191 raise(SIGKILL);
192 }
193 }
194 if (close_all)
195 {
196 closefrom(3);
197 }
198 if (execve(argv[0], argv, envp ?: empty) == -1)
199 {
200 raise(SIGKILL);
201 }
202 /* not reached */
203 default:
204 /* parent */
205 close_if(&this->in[PIPE_READ]);
206 close_if(&this->out[PIPE_WRITE]);
207 close_if(&this->err[PIPE_WRITE]);
208 if (in)
209 {
210 *in = this->in[PIPE_WRITE];
211 this->in[PIPE_WRITE] = -1;
212 }
213 if (out)
214 {
215 *out = this->out[PIPE_READ];
216 this->out[PIPE_READ] = -1;
217 }
218 if (err)
219 {
220 *err = this->err[PIPE_READ];
221 this->err[PIPE_READ] = -1;
222 }
223 return &this->public;
224 }
225 }
226
227 /**
228 * See header
229 */
230 process_t* process_start_shell(char *const envp[], int *in, int *out, int *err,
231 char *fmt, ...)
232 {
233 char *argv[] = {
234 "/bin/sh",
235 "-c",
236 NULL,
237 NULL
238 };
239 process_t *process;
240 va_list args;
241 int len;
242
243 va_start(args, fmt);
244 len = vasprintf(&argv[2], fmt, args);
245 va_end(args);
246 if (len < 0)
247 {
248 return NULL;
249 }
250
251 process = process_start(argv, envp, in, out, err, TRUE);
252 free(argv[2]);
253 return process;
254 }
255
256 #else /* WIN32 */
257
258 /**
259 * Private data of an process_t object.
260 */
261 struct private_process_t {
262
263 /**
264 * Public process_t interface.
265 */
266 process_t public;
267
268 /**
269 * child stdin pipe
270 */
271 HANDLE in[PIPE_ENDS];
272
273 /**
274 * child stdout pipe
275 */
276 HANDLE out[PIPE_ENDS];
277
278 /**
279 * child stderr pipe
280 */
281 HANDLE err[PIPE_ENDS];
282
283 /**
284 * child process information
285 */
286 PROCESS_INFORMATION pi;
287 };
288
289 /**
290 * Clean up state associated to child process
291 */
292 static void process_destroy(private_process_t *this)
293 {
294 if (this->in[PIPE_READ])
295 {
296 CloseHandle(this->in[PIPE_READ]);
297 }
298 if (this->in[PIPE_WRITE])
299 {
300 CloseHandle(this->in[PIPE_WRITE]);
301 }
302 if (this->out[PIPE_READ])
303 {
304 CloseHandle(this->out[PIPE_READ]);
305 }
306 if (this->out[PIPE_WRITE])
307 {
308 CloseHandle(this->out[PIPE_WRITE]);
309 }
310 if (this->err[PIPE_READ])
311 {
312 CloseHandle(this->err[PIPE_READ]);
313 }
314 if (this->err[PIPE_WRITE])
315 {
316 CloseHandle(this->err[PIPE_WRITE]);
317 }
318 if (this->pi.hProcess)
319 {
320 CloseHandle(this->pi.hProcess);
321 CloseHandle(this->pi.hThread);
322 }
323 free(this);
324 }
325
326 METHOD(process_t, wait_, bool,
327 private_process_t *this, int *code)
328 {
329 DWORD ec;
330
331 if (WaitForSingleObject(this->pi.hProcess, INFINITE) != WAIT_OBJECT_0)
332 {
333 DBG1(DBG_LIB, "waiting for child process failed: 0x%08x",
334 GetLastError());
335 process_destroy(this);
336 return FALSE;
337 }
338 if (code)
339 {
340 if (!GetExitCodeProcess(this->pi.hProcess, &ec))
341 {
342 DBG1(DBG_LIB, "getting child process exit code failed: 0x%08x",
343 GetLastError());
344 process_destroy(this);
345 return FALSE;
346 }
347 *code = ec;
348 }
349 process_destroy(this);
350 return TRUE;
351 }
352
353 /**
354 * Append a command line argument to buf, optionally quoted
355 */
356 static void append_arg(char *buf, u_int len, char *arg, char *quote)
357 {
358 char *space = "";
359 int current;
360
361 current = strlen(buf);
362 if (current)
363 {
364 space = " ";
365 }
366 snprintf(buf + current, len - current, "%s%s%s%s", space, quote, arg, quote);
367 }
368
369 /**
370 * Append a null-terminate env string to buf
371 */
372 static void append_env(char *buf, u_int len, char *env)
373 {
374 char *pos = buf;
375 int current;
376
377 while (TRUE)
378 {
379 pos += strlen(pos);
380 if (!pos[1])
381 {
382 if (pos == buf)
383 {
384 current = 0;
385 }
386 else
387 {
388 current = pos - buf + 1;
389 }
390 snprintf(buf + current, len - current, "%s", env);
391 break;
392 }
393 pos++;
394 }
395 }
396
397 /**
398 * See header
399 */
400 process_t* process_start(char *const argv[], char *const envp[],
401 int *in, int *out, int *err, bool close_all)
402 {
403 private_process_t *this;
404 char arg[32768], env[32768];
405 SECURITY_ATTRIBUTES sa = {
406 .nLength = sizeof(SECURITY_ATTRIBUTES),
407 .bInheritHandle = TRUE,
408 };
409 STARTUPINFO sui = {
410 .cb = sizeof(STARTUPINFO),
411 };
412 int i;
413
414 memset(arg, 0, sizeof(arg));
415 memset(env, 0, sizeof(env));
416
417 for (i = 0; argv[i]; i++)
418 {
419 if (!strchr(argv[i], ' '))
420 { /* no spaces, fine for appending */
421 append_arg(arg, sizeof(arg) - 1, argv[i], "");
422 }
423 else if (argv[i][0] == '"' &&
424 argv[i][strlen(argv[i]) - 1] == '"' &&
425 strchr(argv[i] + 1, '"') == argv[i] + strlen(argv[i]) - 1)
426 { /* already properly quoted */
427 append_arg(arg, sizeof(arg) - 1, argv[i], "");
428 }
429 else if (strchr(argv[i], ' ') && !strchr(argv[i], '"'))
430 { /* spaces, but no quotes; append quoted */
431 append_arg(arg, sizeof(arg) - 1, argv[i], "\"");
432 }
433 else
434 {
435 DBG1(DBG_LIB, "invalid command line argument: %s", argv[i]);
436 return NULL;
437 }
438 }
439 if (envp)
440 {
441 for (i = 0; envp[i]; i++)
442 {
443 append_env(env, sizeof(env) - 1, envp[i]);
444 }
445 }
446
447 INIT(this,
448 .public = {
449 .wait = _wait_,
450 },
451 );
452
453 if (in)
454 {
455 sui.dwFlags = STARTF_USESTDHANDLES;
456 if (!CreatePipe(&this->in[PIPE_READ], &this->in[PIPE_WRITE], &sa, 0))
457 {
458 process_destroy(this);
459 return NULL;
460 }
461 if (!SetHandleInformation(this->in[PIPE_WRITE], HANDLE_FLAG_INHERIT, 0))
462 {
463 process_destroy(this);
464 return NULL;
465 }
466 sui.hStdInput = this->in[PIPE_READ];
467 *in = _open_osfhandle((uintptr_t)this->in[PIPE_WRITE], 0);
468 if (*in == -1)
469 {
470 process_destroy(this);
471 return NULL;
472 }
473 }
474 if (out)
475 {
476 sui.dwFlags = STARTF_USESTDHANDLES;
477 if (!CreatePipe(&this->out[PIPE_READ], &this->out[PIPE_WRITE], &sa, 0))
478 {
479 process_destroy(this);
480 return NULL;
481 }
482 if (!SetHandleInformation(this->out[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
483 {
484 process_destroy(this);
485 return NULL;
486 }
487 sui.hStdOutput = this->out[PIPE_WRITE];
488 *out = _open_osfhandle((uintptr_t)this->out[PIPE_READ], 0);
489 if (*out == -1)
490 {
491 process_destroy(this);
492 return NULL;
493 }
494 }
495 if (err)
496 {
497 sui.dwFlags = STARTF_USESTDHANDLES;
498 if (!CreatePipe(&this->err[PIPE_READ], &this->err[PIPE_WRITE], &sa, 0))
499 {
500 process_destroy(this);
501 return NULL;
502 }
503 if (!SetHandleInformation(this->err[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
504 {
505 process_destroy(this);
506 return NULL;
507 }
508 sui.hStdError = this->err[PIPE_WRITE];
509 *err = _open_osfhandle((uintptr_t)this->err[PIPE_READ], 0);
510 if (*err == -1)
511 {
512 process_destroy(this);
513 return NULL;
514 }
515 }
516
517 if (!CreateProcess(argv[0], arg, NULL, NULL, TRUE,
518 NORMAL_PRIORITY_CLASS, env, NULL, &sui, &this->pi))
519 {
520 DBG1(DBG_LIB, "creating process '%s' failed: 0x%08x",
521 argv[0], GetLastError());
522 process_destroy(this);
523 return NULL;
524 }
525
526 /* close child process end of pipes */
527 if (this->in[PIPE_READ])
528 {
529 CloseHandle(this->in[PIPE_READ]);
530 this->in[PIPE_READ] = NULL;
531 }
532 if (this->out[PIPE_WRITE])
533 {
534 CloseHandle(this->out[PIPE_WRITE]);
535 this->out[PIPE_WRITE] = NULL;
536 }
537 if (this->err[PIPE_WRITE])
538 {
539 CloseHandle(this->err[PIPE_WRITE]);
540 this->err[PIPE_WRITE] = NULL;
541 }
542 /* our side gets closed over the osf_handle closed by caller */
543 this->in[PIPE_WRITE] = NULL;
544 this->out[PIPE_READ] = NULL;
545 this->err[PIPE_READ] = NULL;
546 return &this->public;
547 }
548
549 /**
550 * See header
551 */
552 process_t* process_start_shell(char *const envp[], int *in, int *out, int *err,
553 char *fmt, ...)
554 {
555 char path[MAX_PATH], *exe = "system32\\cmd.exe";
556 char *argv[] = {
557 path,
558 "/C",
559 NULL,
560 NULL
561 };
562 process_t *process;
563 va_list args;
564 int len;
565
566 len = GetSystemWindowsDirectory(path, sizeof(path));
567 if (len == 0 || len >= sizeof(path) - strlen(exe))
568 {
569 DBG1(DBG_LIB, "resolving Windows directory failed: 0x%08x",
570 GetLastError());
571 return NULL;
572 }
573 if (path[len + 1] != '\\')
574 {
575 strncat(path, "\\", sizeof(path) - len++);
576 }
577 strncat(path, exe, sizeof(path) - len);
578
579 va_start(args, fmt);
580 len = vasprintf(&argv[2], fmt, args);
581 va_end(args);
582 if (len < 0)
583 {
584 return NULL;
585 }
586
587 process = process_start(argv, envp, in, out, err, TRUE);
588 free(argv[2]);
589 return process;
590 }
591
592 #endif /* WIN32 */