Beispiel #1
0
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
    uv_process_options_t options) {
  int i;
  uv_err_t err = uv_ok_;
  WCHAR* path = NULL;
  BOOL result;
  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
           *env = NULL, *cwd = NULL;
  STARTUPINFOW startup;
  PROCESS_INFORMATION info;
  DWORD process_flags;

  if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
    uv__set_artificial_error(loop, UV_ENOTSUP);
    return -1;
  }

  if (options.file == NULL ||
      options.args == NULL) {
    uv__set_artificial_error(loop, UV_EINVAL);
    return -1;
  }

  assert(options.file != NULL);
  assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
                             UV_PROCESS_DETACHED |
                             UV_PROCESS_SETGID |
                             UV_PROCESS_SETUID)));

  uv_process_init(loop, process);
  process->exit_cb = options.exit_cb;

  err = uv_utf8_to_utf16_alloc(options.file, &application);
  if (err.code != UV_OK)
    goto done;

  err = make_program_args(options.args,
                          options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
                          &arguments);
  if (err.code != UV_OK)
    goto done;

  if (options.cwd) {
    /* Explicit cwd */
    err = uv_utf8_to_utf16_alloc(options.cwd, &cwd);
    if (err.code != UV_OK)
      goto done;

  } else {
    /* Inherit cwd */
    DWORD cwd_len, r;

    cwd_len = GetCurrentDirectoryW(0, NULL);
    if (!cwd_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }

    cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR));
    if (cwd == NULL) {
      err = uv__new_artificial_error(UV_ENOMEM);
      goto done;
    }

    r = GetCurrentDirectoryW(cwd_len, cwd);
    if (r == 0 || r >= cwd_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }
  }

   /* Get PATH environment variable. */
  {
    DWORD path_len, r;

    path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
    if (path_len == 0) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }


    path = (WCHAR*) malloc(path_len * sizeof(WCHAR));
    if (path == NULL) {
      err = uv__new_artificial_error(UV_ENOMEM);
      goto done;
    }

    r = GetEnvironmentVariableW(L"PATH", path, path_len);
    if (r == 0 || r >= path_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }
  }

  application_path = search_path(application,
                                 cwd,
                                 path);
  if (application_path == NULL) {
    /* Not found. */
    err = uv__new_artificial_error(UV_ENOENT);
    goto done;
  }


  err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
  if (err.code != UV_OK)
    goto done;

  startup.cb = sizeof(startup);
  startup.lpReserved = NULL;
  startup.lpDesktop = NULL;
  startup.lpTitle = NULL;
  startup.dwFlags = STARTF_USESTDHANDLES;
  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);

  process_flags = CREATE_UNICODE_ENVIRONMENT;
  if (options.flags & UV_PROCESS_DETACHED) {
    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
  }

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

    /* Set IPC pid to all IPC pipes. */
    for (i = 0; i < options.stdio_count; i++) {
      const uv_stdio_container_t* fdopt = &options.stdio[i];
      if (fdopt->flags & UV_CREATE_PIPE &&
          fdopt->data.stream->type == UV_NAMED_PIPE &&
          ((uv_pipe_t*) fdopt->data.stream)->ipc) {
        ((uv_pipe_t*) fdopt->data.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. */
    err = uv__new_sys_error(GetLastError());
  }

done:
  free(application);
  free(application_path);
  free(arguments);
  free(cwd);
  free(env);
  free(path);

  process->spawn_error = err;

  if (process->child_stdio_buffer != NULL) {
    /* Clean up child stdio handles. */
    uv__stdio_destroy(process->child_stdio_buffer);
    process->child_stdio_buffer = NULL;
  }

  /* Make the handle active. It will remain active until the exit callback */
  /* is made or the handle is closed, whichever happens first. */
  uv__handle_start(process);

  /* If an error happened, queue the exit req. */
  if (err.code != UV_OK) {
    process->exit_cb_pending = 1;
    uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req);
  }

  return 0;
}
Beispiel #2
0
int uv_spawn_jx(uv_loop_t* loop, uv_process_t* process,
                uv_process_options_t* options) {
#ifdef WINONECORE
  error_console("Error: WindowsOneCore does not support spawning a process.\n");
  return -1; // not supported
#else
  int i;
  uv_err_t err = uv_ok_;
  WCHAR* path = NULL;
  BOOL result;
  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
         *env = NULL, *cwd = NULL;
  STARTUPINFOW startup;
  PROCESS_INFORMATION info;
  DWORD process_flags;

  if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
    uv__set_artificial_error(loop, UV_ENOTSUP);
    return -1;
  }

  if (options->file == NULL || options->args == NULL) {
    uv__set_artificial_error(loop, UV_EINVAL);
    return -1;
  }

  assert(options->file != NULL);
  assert(!(options->flags &
           ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID |
             UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));

  uv_process_init(loop, process);
  process->exit_cb = options->exit_cb;

  err = uv_utf8_to_utf16_alloc(options->file, &application);
  if (err.code != UV_OK) goto done;

  err = make_program_args(
      options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
      &arguments);
  if (err.code != UV_OK) goto done;

  if (options->env) {
    err = make_program_env(options->env, &env);
    if (err.code != UV_OK) goto done;
  }

  if (options->cwd) {
    /* Explicit cwd */
    err = uv_utf8_to_utf16_alloc(options->cwd, &cwd);
    if (err.code != UV_OK) goto done;

  } else {
    /* Inherit cwd */
    DWORD cwd_len, r;

    cwd_len = GetCurrentDirectoryW(0, NULL);
    if (!cwd_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }

    cwd = (WCHAR*)malloc(cwd_len * sizeof(WCHAR));
    if (cwd == NULL) {
      err = uv__new_artificial_error(UV_ENOMEM);
      goto done;
    }

    r = GetCurrentDirectoryW(cwd_len, cwd);
    if (r == 0 || r >= cwd_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }
  }

  /* Get PATH environment variable. */
  {
    DWORD path_len, r;

    path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
    if (path_len == 0) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }

    path = (WCHAR*)malloc(path_len * sizeof(WCHAR));
    if (path == NULL) {
      err = uv__new_artificial_error(UV_ENOMEM);
      goto done;
    }

    r = GetEnvironmentVariableW(L"PATH", path, path_len);
    if (r == 0 || r >= path_len) {
      err = uv__new_sys_error(GetLastError());
      goto done;
    }
  }

  application_path = search_path(application, cwd, path);
  if (application_path == NULL) {
    /* Not found. */
    err = uv__new_artificial_error(UV_ENOENT);
    goto done;
  }

  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
  if (err.code != UV_OK) goto done;

  startup.cb = sizeof(startup);
  startup.lpReserved = NULL;
  startup.lpDesktop = NULL;
  startup.lpTitle = NULL;
  startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;

  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
  startup.lpReserved2 = (BYTE*)process->child_stdio_buffer;

  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);

  if (options->flags & UV_PROCESS_WINDOWS_HIDE) {
    /* Use SW_HIDE to avoid any potential process window. */
    startup.wShowWindow = SW_HIDE;
  } else {
    startup.wShowWindow = SW_SHOWDEFAULT;
  }

  process_flags = CREATE_UNICODE_ENVIRONMENT;

  if (options->flags & UV_PROCESS_DETACHED) {
    /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
     * means that libuv might not let you create a fully deamonized process
     * when run under job control. However the type of job control that libuv
     * itself creates doesn't trickle down to subprocesses so they can still
     * daemonize.
     *
     * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
     * CreateProcess call fail if we're under job control that doesn't allow
     * breakaway.
     */
    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
  }

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

    /* If the process isn't spawned as detached, assign to the global job */
    /* object so windows will kill it when the parent process dies. */
    if (!(options->flags & UV_PROCESS_DETACHED)) {
      uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle);

      if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
        /* AssignProcessToJobObject might fail if this process is under job
         * control and the job doesn't have the
         * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
         * that doesn't support nested jobs.
         *
         * When that happens we just swallow the error and continue without
         * establishing a kill-child-on-parent-exit relationship, otherwise
         * there would be no way for libuv applications run under job control
         * to spawn processes at all.
         */
        DWORD err = GetLastError();
        if (err != ERROR_ACCESS_DENIED)
          uv_fatal_error(err, "AssignProcessToJobObject");
      }
    }

    /* Set IPC pid to all IPC pipes. */
    for (i = 0; i < options->stdio_count; i++) {
      const uv_stdio_container_t* fdopt = &options->stdio[i];
      if (fdopt->flags & UV_CREATE_PIPE &&
          fdopt->data.stream->type == UV_NAMED_PIPE &&
          ((uv_pipe_t*)fdopt->data.stream)->ipc) {
        ((uv_pipe_t*)fdopt->data.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. */
    err = uv__new_sys_error(GetLastError());
  }

done:
  JX_FREE(process, application);
  JX_FREE(process, application_path);
  JX_FREE(process, arguments);
  JX_FREE(process, cwd);
  JX_FREE(process, env);
  JX_FREE(process, path);

  process->spawn_error = err;

  if (process->child_stdio_buffer != NULL) {
    /* Clean up child stdio handles. */
    uv__stdio_destroy(process->child_stdio_buffer);
    process->child_stdio_buffer = NULL;
  }

  /* Make the handle active. It will remain active until the exit callback */
  /* is made or the handle is closed, whichever happens first. */
  uv__handle_start(process);

  /* If an error happened, queue the exit req. */
  if (err.code != UV_OK) {
    process->exit_cb_pending = 1;
    uv_insert_pending_req(loop, (uv_req_t*)&process->exit_req);
  }

  return 0;
#endif
}
Beispiel #3
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;
}
Beispiel #4
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 #5
0
int uv_spawn(uv_loop_t* loop,
             uv_process_t* process,
             const uv_process_options_t* options) {
  int i;
  int err = 0;
  WCHAR* path = NULL, *alloc_path = NULL;
  BOOL result;
  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
         *env = NULL, *cwd = NULL;
  STARTUPINFOW startup;
  PROCESS_INFORMATION info;
  DWORD process_flags;

  uv_process_init(loop, process);
  process->exit_cb = options->exit_cb;

  if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
    return UV_ENOTSUP;
  }

  if (options->file == NULL ||
      options->args == NULL) {
    return UV_EINVAL;
  }

  if (options->cpumask != NULL) {
    if (options->cpumask_size < (size_t)uv_cpumask_size()) {
      return UV_EINVAL;
    }
  }

  assert(options->file != NULL);
  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
                              UV_PROCESS_SETGID |
                              UV_PROCESS_SETUID |
                              UV_PROCESS_WINDOWS_HIDE |
                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));

  err = uv_utf8_to_utf16_alloc(options->file, &application);
  if (err)
    goto done;

  err = make_program_args(
      options->args,
      options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
      &arguments);
  if (err)
    goto done;

  if (options->env) {
     err = make_program_env(options->env, &env);
     if (err)
       goto done;
  }

  if (options->cwd) {
    /* Explicit cwd */
    err = uv_utf8_to_utf16_alloc(options->cwd, &cwd);
    if (err)
      goto done;

  } else {
    /* Inherit cwd */
    DWORD cwd_len, r;

    cwd_len = GetCurrentDirectoryW(0, NULL);
    if (!cwd_len) {
      err = GetLastError();
      goto done;
    }

    cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR));
    if (cwd == NULL) {
      err = ERROR_OUTOFMEMORY;
      goto done;
    }

    r = GetCurrentDirectoryW(cwd_len, cwd);
    if (r == 0 || r >= cwd_len) {
      err = GetLastError();
      goto done;
    }
  }

  /* Get PATH environment variable. */
  path = find_path(env);
  if (path == NULL) {
    DWORD path_len, r;

    path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
    if (path_len == 0) {
      err = GetLastError();
      goto done;
    }

    alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
    if (alloc_path == NULL) {
      err = ERROR_OUTOFMEMORY;
      goto done;
    }
    path = alloc_path;

    r = GetEnvironmentVariableW(L"PATH", path, path_len);
    if (r == 0 || r >= path_len) {
      err = GetLastError();
      goto done;
    }
  }

  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
  if (err)
    goto done;

  application_path = search_path(application,
                                 cwd,
                                 path);
  if (application_path == NULL) {
    /* Not found. */
    err = ERROR_FILE_NOT_FOUND;
    goto done;
  }

  startup.cb = sizeof(startup);
  startup.lpReserved = NULL;
  startup.lpDesktop = NULL;
  startup.lpTitle = NULL;
  startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;

  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;

  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);

  process_flags = CREATE_UNICODE_ENVIRONMENT;

  if (options->flags & UV_PROCESS_WINDOWS_HIDE) {
    /* Avoid creating console window if stdio is not inherited. */
    for (i = 0; i < options->stdio_count; i++) {
      if (options->stdio[i].flags & UV_INHERIT_FD)
        break;
      if (i == options->stdio_count - 1)
        process_flags |= CREATE_NO_WINDOW;
    }

    /* Use SW_HIDE to avoid any potential process window. */
    startup.wShowWindow = SW_HIDE;
  } else {
    startup.wShowWindow = SW_SHOWDEFAULT;
  }

  if (options->flags & UV_PROCESS_DETACHED) {
    /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
     * means that libuv might not let you create a fully daemonized process
     * when run under job control. However the type of job control that libuv
     * itself creates doesn't trickle down to subprocesses so they can still
     * daemonize.
     *
     * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
     * CreateProcess call fail if we're under job control that doesn't allow
     * breakaway.
     */
    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
  }

  if (options->cpumask != NULL) {
    /* Create the child in a suspended state so we have a chance to set
       its process affinity before it runs.  */
    process_flags |= CREATE_SUSPENDED;
  }

  if (!CreateProcessW(application_path,
                     arguments,
                     NULL,
                     NULL,
                     1,
                     process_flags,
                     env,
                     cwd,
                     &startup,
                     &info)) {
    /* CreateProcessW failed. */
    err = GetLastError();
    goto done;
  }

  if (options->cpumask != NULL) {
    /* The child is currently suspended.  Set its process affinity
       or terminate it if we can't.  */
    int i;
    int cpumasksize;
    DWORD_PTR sysmask;
    DWORD_PTR oldmask;
    DWORD_PTR newmask;

    cpumasksize = uv_cpumask_size();

    if (!GetProcessAffinityMask(info.hProcess, &oldmask, &sysmask)) {
      err = GetLastError();
      TerminateProcess(info.hProcess, 1);
      goto done;
    }

    newmask = 0;
    for (i = 0; i < cpumasksize; i++) {
      if (options->cpumask[i]) {
        if (oldmask & (((DWORD_PTR)1) << i)) {
          newmask |= ((DWORD_PTR)1) << i;
        } else {
          err = UV_EINVAL;
          TerminateProcess(info.hProcess, 1);
          goto done;
        }
      }
    }

    if (!SetProcessAffinityMask(info.hProcess, newmask)) {
      err = GetLastError();
      TerminateProcess(info.hProcess, 1);
      goto done;
    }

    /* The process affinity of the child is set.  Let it run.  */
    if (ResumeThread(info.hThread) == ((DWORD)-1)) {
      err = GetLastError();
      TerminateProcess(info.hProcess, 1);
      goto done;
    }
  }

  /* Spawn succeeded */
  /* Beyond this point, failure is reported asynchronously. */

  process->process_handle = info.hProcess;
  process->pid = info.dwProcessId;

  /* If the process isn't spawned as detached, assign to the global job */
  /* object so windows will kill it when the parent process dies. */
  if (!(options->flags & UV_PROCESS_DETACHED)) {
    uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle);

    if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
      /* AssignProcessToJobObject might fail if this process is under job
       * control and the job doesn't have the
       * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
       * that doesn't support nested jobs.
       *
       * When that happens we just swallow the error and continue without
       * establishing a kill-child-on-parent-exit relationship, otherwise
       * there would be no way for libuv applications run under job control
       * to spawn processes at all.
       */
      DWORD err = GetLastError();
      if (err != ERROR_ACCESS_DENIED)
        uv_fatal_error(err, "AssignProcessToJobObject");
    }
  }

  /* Set IPC pid to all IPC pipes. */
  for (i = 0; i < options->stdio_count; i++) {
    const uv_stdio_container_t* fdopt = &options->stdio[i];
    if (fdopt->flags & UV_CREATE_PIPE &&
        fdopt->data.stream->type == UV_NAMED_PIPE &&
        ((uv_pipe_t*) fdopt->data.stream)->ipc) {
      ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.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);

  assert(!err);

  /* Make the handle active. It will remain active until the exit callback */
  /* is made or the handle is closed, whichever happens first. */
  uv__handle_start(process);

  /* Cleanup, whether we succeeded or failed. */
 done:
  uv__free(application);
  uv__free(application_path);
  uv__free(arguments);
  uv__free(cwd);
  uv__free(env);
  uv__free(alloc_path);

  if (process->child_stdio_buffer != NULL) {
    /* Clean up child stdio handles. */
    uv__stdio_destroy(process->child_stdio_buffer);
    process->child_stdio_buffer = NULL;
  }

  return uv_translate_sys_error(err);
}