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 }