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