コード例 #1
0
ファイル: process-stdio.c プロジェクト: 4rejin/node
int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options,
    BYTE** buffer_ptr) {
  BYTE* buffer;
  int count, i;

  count = options->stdio_count;

  if (count < 0 || count > 255) {
    /* Only support FDs 0-255 */
    uv__set_artificial_error(loop, UV_ENOTSUP);
    return -1;
  } else if (count < 3) {
    /* There should always be at least 3 stdio handles. */
    count = 3;
  }

  /* Allocate the child stdio buffer */
  buffer = malloc(CHILD_STDIO_SIZE(count));
  if (buffer == NULL) {
    uv__set_artificial_error(loop, UV_ENOMEM);
    return -1;
  }

  /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */
  /* clean up on failure. */
  CHILD_STDIO_COUNT(buffer) = count;
  for (i = 0; i < count; i++) {
    CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
    CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
  }

  for (i = 0; i < count; i++) {
    uv_stdio_container_t fdopt;
    if (i < options->stdio_count) {
      fdopt = options->stdio[i];
    } else {
      fdopt.flags = UV_IGNORE;
    }

    switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
            UV_INHERIT_STREAM)) {
      case UV_IGNORE:
        /* Starting a process with no stdin/stout/stderr can confuse it. */
        /* So no matter what the user specified, we make sure the first */
        /* three FDs are always open in their typical modes, e.g. stdin */
        /* be readable and stdout/err should be writable. For FDs > 2, don't */
        /* do anything - all handles in the stdio buffer are initialized with */
        /* INVALID_HANDLE_VALUE, which should be okay. */
        if (i <= 2) {
          DWORD access = (i == 0) ? FILE_GENERIC_READ :
                                    FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
          if (uv__create_nul_handle(loop,
                                &CHILD_STDIO_HANDLE(buffer, i),
                                access) < 0) {
            goto error;
          }
          CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
        }
        break;

      case UV_CREATE_PIPE: {
        /* Create a pair of two connected pipe ends; one end is turned into */
        /* an uv_pipe_t for use by the parent. The other one is given to */
        /* the child. */
        uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
        HANDLE child_pipe;

        /* Create a new, connected pipe pair. stdio[i].stream should point */
        /* to an uninitialized, but not connected pipe handle. */
        assert(fdopt.data.stream->type == UV_NAMED_PIPE);
        assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
        assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));

        if (uv__create_stdio_pipe_pair(loop,
                                      parent_pipe,
                                      &child_pipe,
                                      fdopt.flags) < 0) {
          goto error;
        }

        CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
        CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
        break;
      }

      case UV_INHERIT_FD: {
        /* Inherit a raw FD. */
        HANDLE child_handle;

        /* Make an inheritable duplicate of the handle. */
        if (uv__duplicate_fd(loop, fdopt.data.fd, &child_handle) < 0) {
          goto error;
        }

        /* Figure out what the type is. */
        switch (GetFileType(child_handle)) {
          case FILE_TYPE_DISK:
            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
            break;

          case FILE_TYPE_PIPE:
            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;

          case FILE_TYPE_CHAR:
          case FILE_TYPE_REMOTE:
            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
            break;

          case FILE_TYPE_UNKNOWN:
            if (GetLastError != 0) {
              uv__set_sys_error(loop, GetLastError());
              CloseHandle(child_handle);
              goto error;
            }
            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
            break;

          default:
            assert(0);
        }

        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
        break;
      }

      case UV_INHERIT_STREAM: {
        /* Use an existing stream as the stdio handle for the child. */
        HANDLE stream_handle, child_handle;
        unsigned char crt_flags;
        uv_stream_t* stream = fdopt.data.stream;

        /* Leech the handle out of the stream. */
        if (stream->type == UV_TTY) {
          stream_handle = ((uv_tty_t*) stream)->handle;
          crt_flags = FOPEN | FDEV;
        } else if (stream->type == UV_NAMED_PIPE &&
                   stream->flags & UV_HANDLE_CONNECTED) {
          stream_handle = ((uv_pipe_t*) stream)->handle;
          crt_flags = FOPEN | FPIPE;
        } else {
          stream_handle = INVALID_HANDLE_VALUE;
          crt_flags = 0;
        }

        if (stream_handle == NULL ||
            stream_handle == INVALID_HANDLE_VALUE) {
          /* The handle is already closed, or not yet created, or the */
          /* stream type is not supported. */
          uv__set_artificial_error(loop, UV_ENOTSUP);
          goto error;
        }

        /* Make an inheritable copy of the handle. */
        if (uv__duplicate_handle(loop,
                             stream_handle,
                             &child_handle) < 0) {
          goto error;
        }

        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
        CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
        break;
      }

      default:
        assert(0);
    }
  }

  *buffer_ptr  = buffer;
  return 0;

 error:
  uv__stdio_destroy(buffer);
  return -1;
}
コード例 #2
0
ファイル: process.c プロジェクト: JarshChen/jxcore
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
}
コード例 #3
0
ファイル: process.c プロジェクト: Ankso/node
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;
}
コード例 #4
0
ファイル: process.c プロジェクト: smanders/CMake
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);
}