Annotation of embedaddon/strongswan/src/libstrongswan/utils/process.c, revision 1.1.1.1
1.1 misho 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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>