Beispiel #1
0
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
    uv_process_options_t options) {
  int err = 0, keep_child_stdio_open = 0;
  wchar_t* path = NULL;
  int size;
  BOOL result;
  wchar_t* application_path = NULL, *application = NULL, *arguments = NULL,
    *env = NULL, *cwd = NULL;
  HANDLE* child_stdio = process->child_stdio;
  STARTUPINFOW startup;
  PROCESS_INFORMATION info;

  if (!options.file) {
    uv__set_artificial_error(loop, UV_EINVAL);
    return -1;
  }

  uv_process_init(loop, process);

  process->exit_cb = options.exit_cb;
  UTF8_TO_UTF16(options.file, application);
  arguments = options.args ? make_program_args(options.args,
      options.windows_verbatim_arguments) : NULL;
  env = options.env ? make_program_env(options.env) : NULL;

  if (options.cwd) {
    UTF8_TO_UTF16(options.cwd, cwd);
  } else {
    size  = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t);
    if (size) {
      cwd = (wchar_t*)malloc(size);
      if (!cwd) {
        uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
      }
      GetCurrentDirectoryW(size, cwd);
    } else {
      uv__set_sys_error(loop, GetLastError());
      err = -1;
      goto done;
    }
  }

  /* Get PATH env. variable. */
  size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1;
  path = (wchar_t*)malloc(size * sizeof(wchar_t));
  if (!path) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  }
  GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t));
  path[size - 1] = L'\0';

  application_path = search_path(application,
                                 cwd,
                                 path);

  if (!application_path) {
    /* CreateProcess will fail, but this allows us to pass this error to */
    /* the user asynchronously. */
    application_path = application;
  }

  /* Create stdio pipes. */
  if (options.stdin_stream) {
    if (options.stdin_stream->ipc) {
      err = uv_create_stdio_pipe_pair(
          loop,
          options.stdin_stream,
          &child_stdio[0],
          PIPE_ACCESS_DUPLEX,
          GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE,
          1);
    } else {
      err = uv_create_stdio_pipe_pair(
          loop,
          options.stdin_stream,
          &child_stdio[0],
          PIPE_ACCESS_OUTBOUND,
          GENERIC_READ | FILE_WRITE_ATTRIBUTES,
          0);
    }
  } else {
    err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]);
  }
  if (err) {
    goto done;
  }

  if (options.stdout_stream) {
    err = uv_create_stdio_pipe_pair(
        loop, options.stdout_stream,
        &child_stdio[1],
        PIPE_ACCESS_INBOUND,
        GENERIC_WRITE,
        0);
  } else {
    err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]);
  }
  if (err) {
    goto done;
  }

  if (options.stderr_stream) {
    err = uv_create_stdio_pipe_pair(
        loop,
        options.stderr_stream,
        &child_stdio[2],
        PIPE_ACCESS_INBOUND,
        GENERIC_WRITE,
        0);
  } else {
    err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]);
  }
  if (err) {
    goto done;
  }

  startup.cb = sizeof(startup);
  startup.lpReserved = NULL;
  startup.lpDesktop = NULL;
  startup.lpTitle = NULL;
  startup.dwFlags = STARTF_USESTDHANDLES;
  startup.cbReserved2 = 0;
  startup.lpReserved2 = NULL;
  startup.hStdInput = child_stdio[0];
  startup.hStdOutput = child_stdio[1];
  startup.hStdError = child_stdio[2];

  if (CreateProcessW(application_path,
                     arguments,
                     NULL,
                     NULL,
                     1,
                     CREATE_UNICODE_ENVIRONMENT,
                     env,
                     cwd,
                     &startup,
                     &info)) {
    /* Spawn succeeded */
    process->process_handle = info.hProcess;
    process->pid = info.dwProcessId;

    if (options.stdin_stream &&
        options.stdin_stream->ipc) {
      options.stdin_stream->ipc_pid = info.dwProcessId;
    }

    /* Setup notifications for when the child process exits. */
    result = RegisterWaitForSingleObject(&process->wait_handle,
        process->process_handle, exit_wait_callback, (void*)process, INFINITE,
        WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
    if (!result) {
      uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
    }

    CloseHandle(info.hThread);

  } else {
    /* CreateProcessW failed, but this failure should be delivered */
    /* asynchronously to retain unix compatibility. So pretent spawn */
    /* succeeded, and start a thread instead that prints an error */
    /* to the child's intended stderr. */
    process->spawn_errno = GetLastError();
    keep_child_stdio_open = 1;
    if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) {
      uv_fatal_error(GetLastError(), "QueueUserWorkItem");
    }
  }

done:
  free(application);
  if (application_path != application) {
    free(application_path);
  }
  free(arguments);
  free(cwd);
  free(env);
  free(path);

  /* Under normal circumstances we should close the stdio handles now - */
  /* the child now has its own duplicates, or something went horribly wrong. */
  /* The only exception is when CreateProcess has failed, then we actually */
  /* need to keep the stdio handles to report the error asynchronously. */
  if (!keep_child_stdio_open) {
    close_child_stdio(process);
  } else {
    /* We're keeping the handles open, the thread pool is going to have */
    /* it's way with them. But at least make them noninheritable. */
    int i;
    for (i = 0; i < COUNTOF(process->child_stdio); i++) {
      SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0);
    }
  }

  if (err) {
    if (process->wait_handle != INVALID_HANDLE_VALUE) {
      UnregisterWait(process->wait_handle);
      process->wait_handle = INVALID_HANDLE_VALUE;
    }

    if (process->process_handle != INVALID_HANDLE_VALUE) {
      CloseHandle(process->process_handle);
      process->process_handle = INVALID_HANDLE_VALUE;
    }
  }

  return err;
}
Beispiel #2
0
int uv_spawn(uv_process_t* process, uv_process_options_t options) {
  int err = 0, i;
  wchar_t* path;
  int size;
  wchar_t* application_path, *application, *arguments, *env, *cwd;
  STARTUPINFOW startup;
  PROCESS_INFORMATION info;

  uv_process_init(process);

  process->exit_cb = options.exit_cb;
  UTF8_TO_UTF16(options.file, application);
  arguments = options.args ? make_program_args(options.args, options.windows_verbatim_arguments) : NULL;
  env = options.env ? make_program_env(options.env) : NULL;

  if (options.cwd) {
    UTF8_TO_UTF16(options.cwd, cwd);
  } else {
    size  = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t);
    if (size) {
      cwd = (wchar_t*)malloc(size);
      if (!cwd) {
        uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
      }
      GetCurrentDirectoryW(size, cwd);
    } else {
      uv_set_sys_error(GetLastError());
      err = -1;
      goto done;
    }
  }

  /* Get PATH env. variable. */
  size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1;
  path = (wchar_t*)malloc(size * sizeof(wchar_t));
  if (!path) {
    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  }
  GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t));
  path[size - 1] = L'\0';

  application_path = search_path(application,
                                 cwd,
                                 path,
                                 DEFAULT_PATH_EXT);

  if (!application_path) {
    /* CreateProcess will fail, but this allows us to pass this error to */
    /* the user asynchronously. */
    application_path = application;
  }

  /* Create stdio pipes. */
  if (options.stdin_stream) {
    err = uv_create_stdio_pipe_pair(options.stdin_stream, &process->stdio_pipes[0].child_pipe, PIPE_ACCESS_OUTBOUND, GENERIC_READ | FILE_WRITE_ATTRIBUTES);
    if (err) {
      goto done;
    }

    process->stdio_pipes[0].server_pipe = options.stdin_stream;
  }

  if (options.stdout_stream) {
    err = uv_create_stdio_pipe_pair(options.stdout_stream, &process->stdio_pipes[1].child_pipe, PIPE_ACCESS_INBOUND, GENERIC_WRITE);
    if (err) {
      goto done;
    }

    process->stdio_pipes[1].server_pipe = options.stdout_stream;
  }

  if (options.stderr_stream) {
    err = uv_create_stdio_pipe_pair(options.stderr_stream, &process->stdio_pipes[2].child_pipe, PIPE_ACCESS_INBOUND, GENERIC_WRITE);
    if (err) {
      goto done;
    }

    process->stdio_pipes[2].server_pipe = options.stderr_stream;
  }

  startup.cb = sizeof(startup);
  startup.lpReserved = NULL;
  startup.lpDesktop = NULL;
  startup.lpTitle = NULL;
  startup.dwFlags = STARTF_USESTDHANDLES;
  startup.cbReserved2 = 0;
  startup.lpReserved2 = NULL;
  startup.hStdInput = process->stdio_pipes[0].child_pipe;
  startup.hStdOutput = process->stdio_pipes[1].child_pipe;
  startup.hStdError = process->stdio_pipes[2].child_pipe;

  if (CreateProcessW(application_path,
                     arguments,
                     NULL,
                     NULL,
                     1,
                     CREATE_UNICODE_ENVIRONMENT,
                     env,
                     cwd,
                     &startup,
                     &info)) {
    /* Spawn succeeded */
    process->process_handle = info.hProcess;
    process->pid = info.dwProcessId;

    /* Setup notifications for when the child process exits. */
    if (!RegisterWaitForSingleObject(&process->wait_handle, process->process_handle,
        exit_wait_callback, (void*)process, INFINITE,
        WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
      uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
    }

    CloseHandle(info.hThread);

  } else {
    /* CreateProcessW failed, but this failure should be delivered */
    /* asynchronously to retain unix compatibility. So pretent spawn */
    /* succeeded, and start a thread instead that prints an error */
    /* to the child's intended stderr. */
    process->spawn_errno = GetLastError();
    if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) {
      uv_fatal_error(GetLastError(), "QueueUserWorkItem");
    }
  }

done:
  free(application);
  if (application_path != application) {
    free(application_path);
  }
  free(arguments);
  free(cwd);
  free(env);
  free(path);

  if (err) {
    for (i = 0; i < COUNTOF(process->stdio_pipes); i++) {
      if (process->stdio_pipes[i].child_pipe != INVALID_HANDLE_VALUE) {
        CloseHandle(process->stdio_pipes[i].child_pipe);
        process->stdio_pipes[i].child_pipe = INVALID_HANDLE_VALUE;
      }
    }

    if (process->wait_handle != INVALID_HANDLE_VALUE) {
      UnregisterWait(process->wait_handle);
      process->wait_handle = INVALID_HANDLE_VALUE;
    }

    if (process->process_handle != INVALID_HANDLE_VALUE) {
      CloseHandle(process->process_handle);
      process->process_handle = INVALID_HANDLE_VALUE;
    }
  }

  return err;
}