moonbridge
changeset 205:5601a486e68a
Support asynchronous I/O with stdin/stdout/stderr of executed child processes
author | jbe |
---|---|
date | Sun Jun 21 20:10:21 2015 +0200 (2015-06-21) |
parents | 7d1cda1ed530 |
children | 56efa825bfc7 |
files | moonbridge_io.c reference.txt |
line diff
1.1 --- a/moonbridge_io.c Sun Jun 21 02:49:23 2015 +0200 1.2 +++ b/moonbridge_io.c Sun Jun 21 20:10:21 2015 +0200 1.3 @@ -18,6 +18,8 @@ 1.4 #include <time.h> 1.5 #include <netdb.h> 1.6 #include <arpa/inet.h> 1.7 +#include <sys/types.h> 1.8 +#include <sys/wait.h> 1.9 1.10 #include <lua.h> 1.11 #include <lauxlib.h> 1.12 @@ -38,6 +40,8 @@ 1.13 #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle" 1.14 #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public" 1.15 #define MOONBR_IO_LISTENER_MT_REGKEY "moonbridge_io_listener" 1.16 +#define MOONBR_IO_CHILD_MT_REGKEY "moonbridge_io_child" 1.17 +#define MOONBR_IO_CHILD_PT_REGKEY "moonbridge_io_child_pt" 1.18 1.19 typedef struct { 1.20 int fd; 1.21 @@ -73,6 +77,13 @@ 1.22 int nonblocking; 1.23 } moonbr_io_listener_t; 1.24 1.25 +typedef struct { 1.26 + pid_t pid; 1.27 + int uninitialized_in; 1.28 + int uninitialized_out; 1.29 + int uninitialized_err; 1.30 +} moonbr_io_child_t; 1.31 + 1.32 static int moonbr_io_yield(lua_State *L) { 1.33 return lua_yield(L, lua_gettop(L)); 1.34 } 1.35 @@ -857,6 +868,7 @@ 1.36 1.37 static int moonbr_io_handleindex(lua_State *L) { 1.38 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); 1.39 + luaL_checkany(L, 2); 1.40 lua_getuservalue(L, 1); 1.41 lua_getfield(L, -1, "public"); 1.42 lua_pushvalue(L, 2); 1.43 @@ -866,6 +878,8 @@ 1.44 1.45 static int moonbr_io_handlenewindex(lua_State *L) { 1.46 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); 1.47 + luaL_checkany(L, 2); 1.48 + luaL_checkany(L, 3); 1.49 lua_getuservalue(L, 1); 1.50 lua_getfield(L, -1, "public"); 1.51 lua_pushvalue(L, 2); 1.52 @@ -1217,6 +1231,265 @@ 1.53 return 0; 1.54 } 1.55 1.56 +static int moonbr_io_exec(lua_State *L) { 1.57 + char **argv; 1.58 + int i, argc; 1.59 + int sockin[2], sockout[2], sockerr[2]; 1.60 + volatile int errorcond = 0; 1.61 + volatile char errmsgbuf[MOONBR_IO_MAXSTRERRORLEN]; 1.62 + moonbr_io_child_t *child; 1.63 + argc = lua_gettop(L); 1.64 + argv = lua_newuserdata(L, (argc + 1) * sizeof(char *)); 1.65 + for (i=0; i<argc; i++) argv[i] = (char *)luaL_checkstring(L, i+1); 1.66 + argv[argc] = NULL; 1.67 + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sockin)) { 1.68 + moonbr_io_errmsg(); 1.69 + lua_pushnil(L); 1.70 + lua_pushfstring(L, "Could not create socket pair: %s", errmsg); 1.71 + return 2; 1.72 + } 1.73 + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sockout)) { 1.74 + moonbr_io_errmsg(); 1.75 + close(sockin[0]); 1.76 + close(sockin[1]); 1.77 + lua_pushnil(L); 1.78 + lua_pushfstring(L, "Could not create socket pair: %s", errmsg); 1.79 + return 2; 1.80 + } 1.81 + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sockerr)) { 1.82 + moonbr_io_errmsg(); 1.83 + close(sockin[0]); 1.84 + close(sockin[1]); 1.85 + close(sockout[0]); 1.86 + close(sockout[1]); 1.87 + lua_pushnil(L); 1.88 + lua_pushfstring(L, "Could not create socket pair: %s", errmsg); 1.89 + return 2; 1.90 + } 1.91 + child = lua_newuserdata(L, sizeof(moonbr_io_child_t)); 1.92 + child->pid = vfork(); 1.93 + if (child->pid == -1) { 1.94 + moonbr_io_errmsg(); 1.95 + close(sockin[0]); 1.96 + close(sockin[1]); 1.97 + close(sockout[0]); 1.98 + close(sockout[1]); 1.99 + close(sockerr[0]); 1.100 + close(sockerr[1]); 1.101 + lua_pushnil(L); 1.102 + lua_pushfstring(L, "Could not fork: %s", errmsg); 1.103 + return 2; 1.104 + } 1.105 + if (!child->pid) { 1.106 + if (dup2(sockin[1], 0) == -1) { 1.107 + errorcond = 1; 1.108 + strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); 1.109 + _exit(0); 1.110 + } 1.111 + if (dup2(sockout[1], 1) == -1) { 1.112 + errorcond = 1; 1.113 + strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); 1.114 + _exit(0); 1.115 + } 1.116 + if (dup2(sockerr[1], 2) == -1) { 1.117 + errorcond = 1; 1.118 + strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); 1.119 + _exit(0); 1.120 + } 1.121 + closefrom(4); 1.122 + if (execvp(argv[0], argv)) { 1.123 + errorcond = 2; 1.124 + strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); 1.125 + _exit(0); 1.126 + } 1.127 + } 1.128 + close(sockin[1]); 1.129 + close(sockout[1]); 1.130 + close(sockerr[1]); 1.131 + if (errorcond) { 1.132 + int status; 1.133 + close(sockin[0]); 1.134 + close(sockout[0]); 1.135 + close(sockerr[0]); 1.136 + while (waitpid(child->pid, &status, 0) == -1) { 1.137 + if (errno != EINTR) { 1.138 + moonbr_io_errmsg(); 1.139 + luaL_error(L, "Error in waitpid call after unsuccessful exec: %s", errmsg); 1.140 + } 1.141 + } 1.142 + lua_pushnil(L); 1.143 + if (errorcond == 2) lua_pushfstring(L, "Could not execute: %s", errmsgbuf); 1.144 + else lua_pushfstring(L, "Error in fork: %s", errmsgbuf); 1.145 + return 2; 1.146 + } 1.147 + if (fcntl(sockin[0], F_SETFD, FD_CLOEXEC) == -1) { 1.148 + moonbr_io_errmsg(); 1.149 + close(sockin[0]); 1.150 + close(sockout[0]); 1.151 + close(sockerr[0]); 1.152 + luaL_error(L, "Error in fcntl call: %s", errmsg); 1.153 + } 1.154 + if (fcntl(sockout[0], F_SETFD, FD_CLOEXEC) == -1) { 1.155 + moonbr_io_errmsg(); 1.156 + close(sockin[0]); 1.157 + close(sockout[0]); 1.158 + close(sockerr[0]); 1.159 + luaL_error(L, "Error in fcntl call: %s", errmsg); 1.160 + } 1.161 + if (fcntl(sockerr[0], F_SETFD, FD_CLOEXEC) == -1) { 1.162 + moonbr_io_errmsg(); 1.163 + close(sockin[0]); 1.164 + close(sockout[0]); 1.165 + close(sockerr[0]); 1.166 + luaL_error(L, "Error in fcntl call: %s", errmsg); 1.167 + } 1.168 + /* close sockets during garbage collection in case a Lua error is raised */ 1.169 + child->uninitialized_in = sockin[0]; 1.170 + child->uninitialized_out = sockout[0]; 1.171 + child->uninitialized_err = sockerr[0]; 1.172 + lua_newtable(L); 1.173 + lua_setuservalue(L, -2); 1.174 + luaL_getmetatable(L, MOONBR_IO_CHILD_MT_REGKEY); 1.175 + lua_setmetatable(L, -2); 1.176 + moonbr_io_pushhandle(L, sockin[0]); 1.177 + lua_setfield(L, -2, "stdin"); 1.178 + child->uninitialized_in = -1; 1.179 + moonbr_io_pushhandle(L, sockout[0]); 1.180 + lua_setfield(L, -2, "stdout"); 1.181 + child->uninitialized_out = -1; 1.182 + moonbr_io_pushhandle(L, sockerr[0]); 1.183 + lua_setfield(L, -2, "stderr"); 1.184 + child->uninitialized_err = -1; 1.185 + return 1; 1.186 +} 1.187 + 1.188 +static int moonbr_io_childindex(lua_State *L) { 1.189 + luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); 1.190 + luaL_checkany(L, 2); 1.191 + lua_getuservalue(L, 1); 1.192 + lua_pushvalue(L, 2); 1.193 + lua_gettable(L, -2); 1.194 + if (lua_isnil(L, -1)) { 1.195 + luaL_getmetatable(L, MOONBR_IO_CHILD_PT_REGKEY); 1.196 + lua_pushvalue(L, 2); 1.197 + lua_gettable(L, -2); 1.198 + } 1.199 + return 1; 1.200 +} 1.201 + 1.202 +static int moonbr_io_childnewindex(lua_State *L) { 1.203 + luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); 1.204 + luaL_checkany(L, 2); 1.205 + luaL_checkany(L, 3); 1.206 + lua_getuservalue(L, 1); 1.207 + lua_pushvalue(L, 2); 1.208 + lua_pushvalue(L, 3); 1.209 + lua_settable(L, -3); 1.210 + return 0; 1.211 +} 1.212 + 1.213 +static int moonbr_io_childgc(lua_State *L) { 1.214 + moonbr_io_child_t *child; 1.215 + child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); 1.216 + if (child->pid) { 1.217 + int status; 1.218 + if (kill(child->pid, SIGKILL)) { 1.219 + moonbr_io_errmsg(); 1.220 + luaL_error(L, "Error in kill call during garbage collection: %s", errmsg); 1.221 + } 1.222 + while (waitpid(child->pid, &status, 0) == -1) { 1.223 + if (errno != EINTR) { 1.224 + moonbr_io_errmsg(); 1.225 + luaL_error(L, "Error in waitpid call during garbage collection: %s", errmsg); 1.226 + } 1.227 + } 1.228 + } 1.229 + if (child->uninitialized_in != -1) close(child->uninitialized_in); 1.230 + if (child->uninitialized_out != -1) close(child->uninitialized_out); 1.231 + if (child->uninitialized_err != -1) close(child->uninitialized_err); 1.232 + return 0; 1.233 +} 1.234 + 1.235 +static int moonbr_io_kill(lua_State *L) { 1.236 + moonbr_io_child_t *child; 1.237 + int sig; 1.238 + child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); 1.239 + sig = luaL_optinteger(L, 2, SIGTERM); 1.240 + if (!child->pid) luaL_error(L, "Attempt to kill an already collected child process"); 1.241 + if (kill(child->pid, sig)) { 1.242 + moonbr_io_errmsg(); 1.243 + luaL_error(L, "Error in kill call: %s", errmsg); 1.244 + } 1.245 + lua_settop(L, 1); 1.246 + return 1; 1.247 +} 1.248 + 1.249 +static int moonbr_io_wait_impl(lua_State *L, int nonblocking) { 1.250 + moonbr_io_child_t *child; 1.251 + pid_t waitedpid; 1.252 + int status; 1.253 + child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); 1.254 + if (!child->pid) luaL_error(L, "Attempt to wait for an already collected child process"); 1.255 + while ((waitedpid = waitpid(child->pid, &status, nonblocking ? WNOHANG : 0)) == -1) { 1.256 + if (errno != EINTR) { 1.257 + moonbr_io_errmsg(); 1.258 + luaL_error(L, "Error in waitpid call: %s", errmsg); 1.259 + } 1.260 + } 1.261 + if (!waitedpid) { 1.262 + lua_pushnil(L); 1.263 + } else { 1.264 + child->pid = 0; 1.265 + if (WIFEXITED(status)) { 1.266 + lua_pushinteger(L, WEXITSTATUS(status)); 1.267 + } else if (WIFSIGNALED(status)) { 1.268 + lua_pushinteger(L, -WTERMSIG(status)); 1.269 + } else { 1.270 + luaL_error(L, "Unexpected status value returned by waitpid call"); 1.271 + } 1.272 + } 1.273 + return 1; 1.274 +} 1.275 + 1.276 +static int moonbr_io_wait(lua_State *L) { 1.277 + return moonbr_io_wait_impl(L, 0); 1.278 +} 1.279 + 1.280 +static int moonbr_io_wait_nb(lua_State *L) { 1.281 + return moonbr_io_wait_impl(L, 1); 1.282 +} 1.283 + 1.284 +#if LUA_VERSION_NUM >= 503 1.285 +static int moonbr_io_wait_cont(lua_State *L, int status, lua_KContext ctx) { 1.286 +#else 1.287 +static int moonbr_io_wait_cont(lua_State *L) { 1.288 +#endif 1.289 +#if !(LUA_VERSION_NUM >= 503) 1.290 + int ctx = 0; 1.291 + lua_getctx(L, &ctx); 1.292 +#endif 1.293 + while (1) { 1.294 + lua_pushcfunction(L, moonbr_io_wait_nb); 1.295 + lua_pushvalue(L, 1); 1.296 + lua_call(L, 1, 1); 1.297 + if (!lua_isnil(L, -1)) break; 1.298 + lua_pushvalue(L, 2); 1.299 + lua_callk(L, 0, 0, ctx, moonbr_io_wait_cont); 1.300 + } 1.301 + return 1; 1.302 +} 1.303 + 1.304 +static int moonbr_io_wait_call(lua_State *L) { 1.305 + lua_settop(L, 2); 1.306 +#if LUA_VERSION_NUM >= 503 1.307 + return moonbr_io_wait_cont(L, 0, 0); 1.308 +#else 1.309 + return moonbr_io_wait_cont(L); 1.310 +#endif 1.311 +} 1.312 + 1.313 +moonbr_io_yield_wrapper(moonbr_io_wait_yield, moonbr_io_wait_call); 1.314 + 1.315 static int moonbr_io_poll(lua_State *L) { 1.316 moonbr_io_handle_t *handle; 1.317 moonbr_io_listener_t *listener; 1.318 @@ -1369,6 +1642,22 @@ 1.319 {NULL, NULL} 1.320 }; 1.321 1.322 +static const struct luaL_Reg moonbr_io_child_methods[] = { 1.323 + {"kill", moonbr_io_kill}, 1.324 + {"wait", moonbr_io_wait}, 1.325 + {"wait_nb", moonbr_io_wait_nb}, 1.326 + {"wait_call", moonbr_io_wait_call}, 1.327 + {"wait_yield", moonbr_io_wait_yield}, 1.328 + {NULL, NULL} 1.329 +}; 1.330 + 1.331 +static const struct luaL_Reg moonbr_io_child_metamethods[] = { 1.332 + {"__index", moonbr_io_childindex}, 1.333 + {"__newindex", moonbr_io_childnewindex}, 1.334 + {"__gc", moonbr_io_childgc}, 1.335 + {NULL, NULL} 1.336 +}; 1.337 + 1.338 static const struct luaL_Reg moonbr_io_module_funcs[] = { 1.339 {"localconnect", moonbr_io_localconnect}, 1.340 {"localconnect_nb", moonbr_io_localconnect_nb}, 1.341 @@ -1376,6 +1665,7 @@ 1.342 {"tcpconnect_nb", moonbr_io_tcpconnect_nb}, 1.343 {"locallisten", moonbr_io_locallisten}, 1.344 {"tcplisten", moonbr_io_tcplisten}, 1.345 + {"exec", moonbr_io_exec}, 1.346 {"poll", moonbr_io_poll}, 1.347 {"timeref", moonbr_io_timeref}, 1.348 {NULL, NULL} 1.349 @@ -1412,6 +1702,24 @@ 1.350 lua_setfield(L, -3, "listener_mt"); 1.351 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_LISTENER_MT_REGKEY); 1.352 1.353 + lua_newtable(L); // child methods 1.354 + luaL_setfuncs(L, moonbr_io_child_methods, 0); 1.355 + lua_pushvalue(L, -1); 1.356 + lua_setfield(L, -3, "child_pt"); 1.357 + lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_CHILD_PT_REGKEY); 1.358 + lua_newtable(L); // child metatable 1.359 + luaL_setfuncs(L, moonbr_io_child_metamethods, 0); 1.360 + lua_pushvalue(L, -1); 1.361 + lua_setfield(L, -3, "child_mt"); 1.362 + lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_CHILD_MT_REGKEY); 1.363 + 1.364 + moonbr_io_pushhandle(L, 0); 1.365 + lua_setfield(L, -2, "stdin"); 1.366 + moonbr_io_pushhandle(L, 1); 1.367 + lua_setfield(L, -2, "stdout"); 1.368 + moonbr_io_pushhandle(L, 2); 1.369 + lua_setfield(L, -2, "stderr"); 1.370 + 1.371 luaL_setfuncs(L, moonbr_io_module_funcs, 0); 1.372 return 1; 1.373
2.1 --- a/reference.txt Sun Jun 21 02:49:23 2015 +0200 2.2 +++ b/reference.txt Sun Jun 21 20:10:21 2015 +0200 2.3 @@ -292,6 +292,33 @@ 2.4 listed below. 2.5 2.6 2.7 +### moonbridge_io.exec(command, arg1, arg2, ...) 2.8 + 2.9 +Executes the given command and returns a handle with three sockets named 2.10 +"stdin", "stdout", and "stderr" as well as the following methods: 2.11 + 2.12 +- :kill(signal) 2.13 +- :wait() 2.14 +- :wait_nb() 2.15 +- :wait_call(waitfunc) 2.16 +- :wait_yield() 2.17 + 2.18 +Use :kill(signal) to terminate the process with the given signal (defaults to 2.19 +SIGTERM). 2.20 + 2.21 +The :wait() method will wait for the process to terminate and return its exit 2.22 +code. If the process was terminated by a signal, a negative integer is returned 2.23 +which corresponds to the respective positive signal number. 2.24 + 2.25 +The method :wait_nb() is the same as :wait(), except that it does not block but 2.26 +returns nil if the child process has not terminated yet. 2.27 + 2.28 +The method :wait_call() is the same as :wait() but calls waitfunc() (in an 2.29 +infinite loop) as long as the process is still running. 2.30 + 2.31 +The method :wait_yield() is an alias for :wait_call(coroutine.yield). 2.32 + 2.33 + 2.34 ### moonbridge_io.localconnect(path) 2.35 2.36 Tries to connect to a local socket (also known as Unix Domain Socket). Returns