Annotation of embedaddon/strongswan/src/libstrongswan/utils/process.c, revision 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>