1 // This is an open source non-commercial project. Dear PVS-Studio, please check
2 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3 
4 #include <assert.h>
5 #include <uv.h>
6 
7 #include "nvim/event/libuv_process.h"
8 #include "nvim/event/loop.h"
9 #include "nvim/event/process.h"
10 #include "nvim/event/rstream.h"
11 #include "nvim/event/wstream.h"
12 #include "nvim/log.h"
13 #include "nvim/macros.h"
14 #include "nvim/os/os.h"
15 
16 #ifdef INCLUDE_GENERATED_DECLARATIONS
17 # include "event/libuv_process.c.generated.h"
18 #endif
19 
20 /// @returns zero on success, or negative error code
libuv_process_spawn(LibuvProcess * uvproc)21 int libuv_process_spawn(LibuvProcess *uvproc)
22   FUNC_ATTR_NONNULL_ALL
23 {
24   Process *proc = (Process *)uvproc;
25   uvproc->uvopts.file = proc->argv[0];
26   uvproc->uvopts.args = proc->argv;
27   uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
28 #ifdef WIN32
29   // libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe
30   // expects a different syntax (must be prepared by the caller before now).
31   if (os_shell_is_cmdexe(proc->argv[0])) {
32     uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
33   }
34   if (proc->detach) {
35     uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
36   }
37 #else
38   // Always setsid() on unix-likes. #8107
39   uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
40 #endif
41   uvproc->uvopts.exit_cb = exit_cb;
42   uvproc->uvopts.cwd = proc->cwd;
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   uvproc->uv.data = proc;
49 
50   if (proc->env) {
51     uvproc->uvopts.env = tv_dict_to_env(proc->env);
52   } else {
53     uvproc->uvopts.env = NULL;
54   }
55 
56   if (!proc->in.closed) {
57     uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
58 #ifdef WIN32
59     uvproc->uvstdio[0].flags |= proc->overlapped ? UV_OVERLAPPED_PIPE : 0;
60 #endif
61     uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t,
62                                                  &proc->in.uv.pipe);
63   }
64 
65   if (!proc->out.closed) {
66     uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
67 #ifdef WIN32
68     // pipe must be readable for IOCP to work on Windows.
69     uvproc->uvstdio[1].flags |= proc->overlapped ?
70                                 (UV_READABLE_PIPE | UV_OVERLAPPED_PIPE) : 0;
71 #endif
72     uvproc->uvstdio[1].data.stream = STRUCT_CAST(uv_stream_t,
73                                                  &proc->out.uv.pipe);
74   }
75 
76   if (!proc->err.closed) {
77     uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
78     uvproc->uvstdio[2].data.stream = STRUCT_CAST(uv_stream_t,
79                                                  &proc->err.uv.pipe);
80   }
81 
82   int status;
83   if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
84     ELOG("uv_spawn(%s) failed: %s", uvproc->uvopts.file, uv_strerror(status));
85     if (uvproc->uvopts.env) {
86       os_free_fullenv(uvproc->uvopts.env);
87     }
88     return status;
89   }
90 
91   proc->pid = uvproc->uv.pid;
92   return status;
93 }
94 
libuv_process_close(LibuvProcess * uvproc)95 void libuv_process_close(LibuvProcess *uvproc)
96   FUNC_ATTR_NONNULL_ARG(1)
97 {
98   uv_close((uv_handle_t *)&uvproc->uv, close_cb);
99 }
100 
close_cb(uv_handle_t * handle)101 static void close_cb(uv_handle_t *handle)
102 {
103   Process *proc = handle->data;
104   if (proc->internal_close_cb) {
105     proc->internal_close_cb(proc);
106   }
107   LibuvProcess *uvproc = (LibuvProcess *)proc;
108   if (uvproc->uvopts.env) {
109     os_free_fullenv(uvproc->uvopts.env);
110   }
111 }
112 
exit_cb(uv_process_t * handle,int64_t status,int term_signal)113 static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
114 {
115   Process *proc = handle->data;
116 #if defined(WIN32)
117   // Use stored/expected signal.
118   term_signal = proc->exit_signal;
119 #endif
120   proc->status = term_signal ? 128 + term_signal : (int)status;
121   proc->internal_exit_cb(proc);
122 }
123