1 /*
2  *  Copyright 2014 The Luvit Authors. All Rights Reserved.
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  */
17 #include "private.h"
18 #include <math.h>
19 
luv_disable_stdio_inheritance(lua_State * L)20 static int luv_disable_stdio_inheritance(lua_State* L) {
21   (void)L;
22   uv_disable_stdio_inheritance();
23   return 0;
24 }
25 
luv_check_process(lua_State * L,int index)26 static uv_process_t* luv_check_process(lua_State* L, int index) {
27   uv_process_t* handle = (uv_process_t*)luv_checkudata(L, index, "uv_process");
28   luaL_argcheck(L, handle->type == UV_PROCESS && handle->data, index, "Expected uv_process_t");
29   return handle;
30 }
31 
exit_cb(uv_process_t * handle,int64_t exit_status,int term_signal)32 static void exit_cb(uv_process_t* handle, int64_t exit_status, int term_signal) {
33   luv_handle_t* data = (luv_handle_t*)handle->data;
34   lua_State* L = data->ctx->L;
35   lua_pushinteger(L, exit_status);
36   lua_pushinteger(L, term_signal);
37   luv_call_callback(L, data, LUV_EXIT, 2);
38 }
39 
luv_spawn_close_cb(uv_handle_t * handle)40 static void luv_spawn_close_cb(uv_handle_t* handle) {
41   luv_handle_t* data = (luv_handle_t*)handle->data;
42   lua_State* L = data->ctx->L;
43   luv_unref_handle(L, (luv_handle_t*)handle->data);
44 }
45 
luv_clean_options(lua_State * L,uv_process_options_t * options,int * args_refs)46 static void luv_clean_options(lua_State* L, uv_process_options_t* options, int* args_refs) {
47   free(options->args);
48   free(options->stdio);
49   free(options->env);
50   if (args_refs) {
51     int i;
52     for (i = 0; args_refs[i] != LUA_NOREF; i++) {
53       luaL_unref(L, LUA_REGISTRYINDEX, args_refs[i]);
54     }
55     free(args_refs);
56   }
57 }
58 
59 // iterates over the tbl to find the max integer
60 // key of the table that is >= 1. If any key is
61 // NOT an integer, simply call lua_rawlen().
sparse_rawlen(lua_State * L,int tbl)62 static int sparse_rawlen(lua_State* L, int tbl) {
63   int len = 0;
64   tbl = lua_absindex(L, tbl);
65 
66   lua_pushnil(L);
67   while (lua_next(L, -2)) {
68     if (lua_type(L, -2) == LUA_TNUMBER) {
69       int idx = lua_tonumber(L, -2);
70       if (floor(idx) == idx && idx >= 1) {
71         if (idx > len) len = idx;
72         lua_pop(L, 1);
73         continue;
74       }
75     }
76     lua_pop(L, 2);
77     return lua_rawlen(L, tbl);
78   }
79   return len;
80 }
81 
luv_spawn(lua_State * L)82 static int luv_spawn(lua_State* L) {
83   uv_process_t* handle;
84   uv_process_options_t options;
85   int* args_refs = NULL;
86   size_t i, len = 0;
87   int ret;
88   luv_ctx_t* ctx = luv_context(L);
89 
90   memset(&options, 0, sizeof(options));
91   options.exit_cb = exit_cb;
92   options.file = luaL_checkstring(L, 1);
93   options.flags = 0;
94 
95   // Make sure the 2nd argument is a table
96   luaL_checktype(L, 2, LUA_TTABLE);
97 
98   // get the args list
99   lua_getfield(L, 2, "args");
100   // +1 for inserted command at front
101   if (lua_type(L, -1) == LUA_TTABLE) {
102     len = 1 + lua_rawlen(L, -1);
103   }
104   else if (lua_type(L, -1) != LUA_TNIL) {
105     luv_clean_options(L, &options, args_refs);
106     return luaL_argerror(L, 3, "args option must be table");
107   }
108   else {
109     len = 1;
110   }
111   // +1 for null terminator at end
112   options.args = (char**)malloc((len + 1) * sizeof(*options.args));
113 
114   // args must be referenced to ensure that they don't get garbage
115   // collected between now and when they are used in uv_spawn.
116   // However, we don't need to ref args[0] since we don't pop that
117   // from the stack. Note: args_refs is a LUA_NOREF-terminated array
118   // when it is non-NULL
119   if (len > 1) {
120     args_refs = (int*)malloc(len * sizeof(int));
121     if (args_refs)
122       args_refs[len-1] = LUA_NOREF;
123   }
124 
125   if (!options.args || (len > 1 && !args_refs)) {
126     luv_clean_options(L, &options, args_refs);
127     return luaL_error(L, "Problem allocating args");
128   }
129   options.args[0] = (char*)options.file;
130   for (i = 1; i < len; ++i) {
131     lua_rawgeti(L, -1, i);
132     options.args[i] = (char*)lua_tostring(L, -1);
133     args_refs[i - 1] = luaL_ref(L, LUA_REGISTRYINDEX);
134   }
135   options.args[len] = NULL;
136   lua_pop(L, 1); // pop the args field (either table or nil)
137 
138   // get the stdio list
139   lua_getfield(L, 2, "stdio");
140   if (lua_type(L, -1) == LUA_TTABLE) {
141     options.stdio_count = len = sparse_rawlen(L, -1);
142     options.stdio = (uv_stdio_container_t*)malloc(len * sizeof(*options.stdio));
143     if (!options.stdio) {
144       luv_clean_options(L, &options, args_refs);
145       return luaL_error(L, "Problem allocating stdio");
146     }
147     for (i = 0; i < len; ++i) {
148       lua_rawgeti(L, -1, i + 1);
149       // integers are assumed to be file descripters
150       if (lua_type(L, -1) == LUA_TNUMBER) {
151         options.stdio[i].flags = UV_INHERIT_FD;
152         options.stdio[i].data.fd = lua_tointeger(L, -1);
153       }
154       // userdata is assumed to be a uv_stream_t instance
155       else if (lua_type(L, -1) == LUA_TUSERDATA) {
156         uv_os_fd_t fd;
157         uv_stream_t* stream = luv_check_stream(L, -1);
158         int err = uv_fileno((uv_handle_t*)stream, &fd);
159         if (err == UV_EINVAL || err == UV_EBADF) {
160           // stdin (fd 0) is read-only, stdout and stderr (fds 1 & 2) are
161           // write-only, and all fds > 2 are read-write
162           int flags = UV_CREATE_PIPE;
163           if (i == 0 || i > 2)
164             flags |= UV_READABLE_PIPE;
165           if (i != 0)
166             flags |= UV_WRITABLE_PIPE;
167           options.stdio[i].flags = (uv_stdio_flags)flags;
168         }
169         else {
170           options.stdio[i].flags = UV_INHERIT_STREAM;
171         }
172         options.stdio[i].data.stream = stream;
173       }
174       else if (lua_type(L, -1) == LUA_TNIL) {
175         options.stdio[i].flags = UV_IGNORE;
176       }
177       else {
178         luv_clean_options(L, &options, args_refs);
179         return luaL_argerror(L, 2, "stdio table entries must be nil, uv_stream_t, or integer");
180       }
181       lua_pop(L, 1);
182     }
183   }
184   else if (lua_type(L, -1) != LUA_TNIL) {
185     luv_clean_options(L, &options, args_refs);
186     return luaL_argerror(L, 2, "stdio option must be table");
187   }
188   lua_pop(L, 1);
189 
190   // Get the env
191   lua_getfield(L, 2, "env");
192   if (lua_type(L, -1) == LUA_TTABLE) {
193     len = lua_rawlen(L, -1);
194     options.env = (char**)malloc((len + 1) * sizeof(*options.env));
195     if (!options.env) {
196       luv_clean_options(L, &options, args_refs);
197       return luaL_error(L, "Problem allocating env");
198     }
199     for (i = 0; i < len; ++i) {
200       lua_rawgeti(L, -1, i + 1);
201       options.env[i] = (char*)lua_tostring(L, -1);
202       lua_pop(L, 1);
203     }
204     options.env[len] = NULL;
205   }
206   else if (lua_type(L, -1) != LUA_TNIL) {
207     luv_clean_options(L, &options, args_refs);
208     return luaL_argerror(L, 2, "env option must be table");
209   }
210   lua_pop(L, 1);
211 
212   // Get the cwd
213   lua_getfield(L, 2, "cwd");
214   if (lua_type(L, -1) == LUA_TSTRING) {
215     options.cwd = (char*)lua_tostring(L, -1);
216   }
217   else if (lua_type(L, -1) != LUA_TNIL) {
218     luv_clean_options(L, &options, args_refs);
219     return luaL_argerror(L, 2, "cwd option must be string");
220   }
221   lua_pop(L, 1);
222 
223   // Check for uid
224   lua_getfield(L, 2, "uid");
225   if (lua_type(L, -1) == LUA_TNUMBER) {
226     options.uid = lua_tointeger(L, -1);
227     options.flags |= UV_PROCESS_SETUID;
228   }
229   else if (lua_type(L, -1) != LUA_TNIL) {
230     luv_clean_options(L, &options, args_refs);
231     return luaL_argerror(L, 2, "uid option must be number");
232   }
233   lua_pop(L, 1);
234 
235   // Check for gid
236   lua_getfield(L, 2, "gid");
237   if (lua_type(L, -1) == LUA_TNUMBER) {
238     options.gid = lua_tointeger(L, -1);
239     options.flags |= UV_PROCESS_SETGID;
240   }
241   else if (lua_type(L, -1) != LUA_TNIL) {
242     luv_clean_options(L, &options, args_refs);
243     return luaL_argerror(L, 2, "gid option must be number");
244   }
245   lua_pop(L, 1);
246 
247   // Check for the boolean flags
248   lua_getfield(L, 2, "verbatim");
249   if (lua_toboolean(L, -1)) {
250     options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
251   }
252   lua_pop(L, 1);
253   lua_getfield(L, 2, "detached");
254   if (lua_toboolean(L, -1)) {
255     options.flags |= UV_PROCESS_DETACHED;
256   }
257   lua_pop(L, 1);
258   lua_getfield(L, 2, "hide");
259   if (lua_toboolean(L, -1)) {
260     options.flags |= UV_PROCESS_WINDOWS_HIDE;
261   }
262   lua_pop(L, 1);
263 #if LUV_UV_VERSION_GEQ(1, 24, 0)
264   lua_getfield(L, 2, "hide_console");
265   if (lua_toboolean(L, -1)) {
266     options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE;
267   }
268   lua_pop(L, 1);
269   lua_getfield(L, 2, "hide_gui");
270   if (lua_toboolean(L, -1)) {
271     options.flags |= UV_PROCESS_WINDOWS_HIDE_GUI;
272   }
273   lua_pop(L, 1);
274 #endif
275 
276   // this will fill the 3rd argument with nil if it doesn't exist so that
277   // the uv_process_t userdata doesn't get treated as the 3rd argument
278   lua_settop(L, 3);
279 
280   handle = (uv_process_t*)luv_newuserdata(L, sizeof(*handle));
281   handle->type = UV_PROCESS;
282   handle->data = luv_setup_handle(L, ctx);
283 
284   if (!lua_isnoneornil(L, 3)) {
285     luv_check_callback(L, (luv_handle_t*)handle->data, LUV_EXIT, 3);
286   }
287 
288   ret = uv_spawn(ctx->loop, handle, &options);
289 
290   luv_clean_options(L, &options, args_refs);
291   if (ret < 0) {
292     /* The async callback is required here because luajit GC may reclaim the
293      * luv handle before libuv is done closing it down.
294      */
295     uv_close((uv_handle_t*)handle, luv_spawn_close_cb);
296     return luv_error(L, ret);
297   }
298   lua_pushinteger(L, handle->pid);
299   return 2;
300 }
301 
luv_parse_signal(lua_State * L,int slot)302 static int luv_parse_signal(lua_State* L, int slot) {
303   if (lua_isnumber(L, slot)) {
304     return lua_tonumber(L, slot);
305   }
306   if (lua_isstring(L, slot)) {
307     return luv_sig_string_to_num(lua_tostring(L, slot));
308   }
309   return SIGTERM;
310 }
311 
luv_process_kill(lua_State * L)312 static int luv_process_kill(lua_State* L) {
313   uv_process_t* handle = luv_check_process(L, 1);
314   int signum = luv_parse_signal(L, 2);
315   int ret = uv_process_kill(handle, signum);
316   return luv_result(L, ret);
317 }
318 
luv_kill(lua_State * L)319 static int luv_kill(lua_State* L) {
320   int pid = luaL_checkinteger(L, 1);
321   int signum = luv_parse_signal(L, 2);
322   int ret = uv_kill(pid, signum);
323   return luv_result(L, ret);
324 }
325 
326 #if LUV_UV_VERSION_GEQ(1, 19, 0)
luv_process_get_pid(lua_State * L)327 static int luv_process_get_pid(lua_State* L) {
328   uv_process_t* handle = luv_check_process(L, 1);
329   lua_pushinteger(L, uv_process_get_pid(handle));
330   return 1;
331 }
332 #endif
333