neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

libuv_proc.c (4781B)


      1 #include <assert.h>
      2 #include <locale.h>
      3 #include <stdint.h>
      4 #include <uv.h>
      5 
      6 #include "nvim/eval/typval.h"
      7 #include "nvim/event/defs.h"
      8 #include "nvim/event/libuv_proc.h"
      9 #include "nvim/event/loop.h"
     10 #include "nvim/event/proc.h"
     11 #include "nvim/log.h"
     12 #include "nvim/os/os.h"
     13 #include "nvim/os/os_defs.h"
     14 #include "nvim/types_defs.h"
     15 #include "nvim/ui_client.h"
     16 
     17 #include "event/libuv_proc.c.generated.h"
     18 
     19 /// @returns zero on success, or negative error code
     20 int libuv_proc_spawn(LibuvProc *uvproc)
     21  FUNC_ATTR_NONNULL_ALL
     22 {
     23  Proc *proc = (Proc *)uvproc;
     24  uvproc->uvopts.file = proc_get_exepath(proc);
     25  uvproc->uvopts.args = proc->argv;
     26  uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
     27 #ifdef MSWIN
     28  // libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe
     29  // expects a different syntax (must be prepared by the caller before now).
     30  if (os_shell_is_cmdexe(proc->argv[0])) {
     31    uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
     32  }
     33  if (proc->detach) {
     34    uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
     35  }
     36 #else
     37  // Always setsid() on unix-likes. #8107
     38  uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
     39 #endif
     40  uvproc->uvopts.exit_cb = exit_cb;
     41  uvproc->uvopts.cwd = proc->cwd;
     42 
     43  uvproc->uvopts.stdio = uvproc->uvstdio;
     44  uvproc->uvopts.stdio_count = 3;
     45  uvproc->uvstdio[0].flags = UV_IGNORE;
     46  uvproc->uvstdio[1].flags = UV_IGNORE;
     47  uvproc->uvstdio[2].flags = UV_IGNORE;
     48 
     49  if (ui_client_forward_stdin) {
     50    assert(UI_CLIENT_STDIN_FD == 3);
     51    uvproc->uvopts.stdio_count = 4;
     52    uvproc->uvstdio[3].data.fd = 0;
     53    uvproc->uvstdio[3].flags = UV_INHERIT_FD;
     54  }
     55  uvproc->uv.data = proc;
     56 
     57  if (proc->env) {
     58    uvproc->uvopts.env = tv_dict_to_env(proc->env);
     59  } else {
     60    uvproc->uvopts.env = NULL;
     61  }
     62 
     63  int to_close[3] = { -1, -1, -1 };
     64 
     65  if (!proc->in.closed) {
     66    uv_file pipe_pair[2];
     67    int client_flags = 0;
     68 #ifdef MSWIN
     69    client_flags |= proc->overlapped ? UV_NONBLOCK_PIPE : 0;
     70 #endif
     71 
     72    // As of libuv 1.51, UV_CREATE_PIPE can only create pipes
     73    // using socketpair(), not pipe(). We want the latter on linux
     74    // as socket pairs behave different in some confusing ways, like
     75    // breaking /proc/0/fd/0 which is disowned by the linux socket maintainer.
     76    uv_pipe(pipe_pair, client_flags, UV_NONBLOCK_PIPE);
     77 
     78    uvproc->uvstdio[0].flags = UV_INHERIT_FD;
     79    uvproc->uvstdio[0].data.fd = pipe_pair[0];
     80    to_close[0] = pipe_pair[0];
     81 
     82    uv_pipe_open(&proc->in.uv.pipe, pipe_pair[1]);
     83  }
     84 
     85  if (!proc->out.s.closed) {
     86 #ifdef MSWIN
     87    // TODO(bfredl): in theory it would have been nice if the uv_pipe() branch
     88    // also worked for windows but IOCP happens because of reasons.
     89    uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
     90    // pipe must be readable for IOCP to work on Windows.
     91    uvproc->uvstdio[1].flags |= proc->overlapped
     92                                ? (UV_READABLE_PIPE | UV_OVERLAPPED_PIPE) : 0;
     93    uvproc->uvstdio[1].data.stream = (uv_stream_t *)(&proc->out.s.uv.pipe);
     94 #else
     95    uv_file pipe_pair[2];
     96    uv_pipe(pipe_pair, UV_NONBLOCK_PIPE, 0);
     97 
     98    uvproc->uvstdio[1].flags = UV_INHERIT_FD;
     99    uvproc->uvstdio[1].data.fd = pipe_pair[1];
    100    to_close[1] = pipe_pair[1];
    101 
    102    uv_pipe_open(&proc->out.s.uv.pipe, pipe_pair[0]);
    103 #endif
    104  }
    105 
    106  if (!proc->err.s.closed) {
    107    uv_file pipe_pair[2];
    108    uv_pipe(pipe_pair, UV_NONBLOCK_PIPE, 0);
    109 
    110    uvproc->uvstdio[2].flags = UV_INHERIT_FD;
    111    uvproc->uvstdio[2].data.fd = pipe_pair[1];
    112    to_close[2] = pipe_pair[1];
    113 
    114    uv_pipe_open(&proc->err.s.uv.pipe, pipe_pair[0]);
    115  }
    116 
    117  int status;
    118  if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
    119    ILOG("uv_spawn(%s) failed: %s", uvproc->uvopts.file, uv_strerror(status));
    120    if (uvproc->uvopts.env) {
    121      os_free_fullenv(uvproc->uvopts.env);
    122    }
    123    goto exit;
    124  }
    125 
    126  proc->pid = uvproc->uv.pid;
    127 exit:
    128  for (int i = 0; i < 3; i++) {
    129    if (to_close[i] > -1) {
    130      close(to_close[i]);
    131    }
    132  }
    133  return status;
    134 }
    135 
    136 void libuv_proc_close(LibuvProc *uvproc)
    137  FUNC_ATTR_NONNULL_ARG(1)
    138 {
    139  uv_close((uv_handle_t *)&uvproc->uv, close_cb);
    140 }
    141 
    142 static void close_cb(uv_handle_t *handle)
    143 {
    144  Proc *proc = handle->data;
    145  if (proc->internal_close_cb) {
    146    proc->internal_close_cb(proc);
    147  }
    148  LibuvProc *uvproc = (LibuvProc *)proc;
    149  if (uvproc->uvopts.env) {
    150    os_free_fullenv(uvproc->uvopts.env);
    151  }
    152 }
    153 
    154 static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
    155 {
    156  Proc *proc = handle->data;
    157 #if defined(MSWIN)
    158  // Use stored/expected signal.
    159  term_signal = proc->exit_signal;
    160 #endif
    161  proc->status = term_signal ? 128 + term_signal : (int)status;
    162  proc->internal_exit_cb(proc);
    163 }
    164 
    165 LibuvProc libuv_proc_init(Loop *loop, void *data)
    166 {
    167  LibuvProc rv = {
    168    .proc = proc_init(loop, kProcTypeUv, data)
    169  };
    170  return rv;
    171 }