/* * gcsd * * gcsd the generic command sequencer daemon can be used to serialize * commands sent over various paralell communication channels to a * single command output. It can be seen as a multiplexer for any * kind of communication between a single resource and various clients * which want to submit commands to it or query information from it. * gcsd is written in C and Lua. The goal is to provide an easy to * understand high level API based on Lua which can be used to * implement the business logic of the so formed multiplexer daemon. * * * Copyright (C) 2009-2010 Markus Grueneis * Christian Pointner * * This file is part of gcsd. * * gcsd is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * gcsd is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with gcsd. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "l_util.h" #if LUA_VERSION_NUM < 502 #define lua_rawlen lua_objlen #endif static int get_fd(lua_State *L) { if(!lua_istable(L, -1)) return -1; lua_pushliteral(L, "fd"); lua_gettable(L, -2); int fd = luaL_checkint(L, -1); lua_pop(L, 1); return fd; } static int collect_fds(lua_State *L, int table_idx, fd_set* fds, int max_fd) { FD_ZERO(fds); int i; for(i = 1;;i++) { int fd; lua_pushinteger(L, i); lua_gettable(L, table_idx); if(lua_isnil(L,-1)) { lua_pop(L, 1); break; } fd = get_fd(L); if(fd >= 0) { FD_SET(fd, fds); max_fd = fd > max_fd ? fd : max_fd; } lua_pop(L,1); } return max_fd; } static void return_fds(lua_State *L, int table_idx, fd_set* fds) { lua_newtable(L); int i, j = 1; for(i = 1;;i++) { int fd; lua_pushinteger(L, i); lua_gettable(L, table_idx); if(lua_isnil(L,-1)) { lua_pop(L, 1); break; } fd = get_fd(L); if(fd >= 0 && FD_ISSET(fd, fds)) { lua_pushinteger(L, j++); lua_insert(L, -2); lua_settable(L, -3); } else lua_pop(L,1); } } static int l_util_select(lua_State *L) { if(!lua_istable(L, 1)) luaL_error(L, "table expected as first argument"); if(!lua_istable(L, 2)) luaL_error(L, "table expected as second argument"); int ms = luaL_optint(L, 3, -1); fd_set readables, writeables; int max_fd = collect_fds(L, 1, &readables, 0); max_fd = collect_fds(L, 2, &writeables, max_fd); struct timeval tv; struct timeval* timeout = NULL; if(ms >= 0) { tv.tv_sec = ms / 1000; tv.tv_usec = (ms % 1000) * 1000; timeout = &tv; } int ret = select(max_fd+1, &readables, &writeables, NULL, timeout); if(ret <= 0) { lua_pushnil(L); lua_pushnil(L); if(!ret) lua_pushliteral(L, "timeout"); else if(errno == EINTR) lua_pushliteral(L, "signal"); else lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 3; } return_fds(L, 1, &readables); return_fds(L, 2, &writeables); return 2; } static char** table_to_argv(lua_State *L, const char* script, int index) { size_t n = 0; if(lua_istable(L, index)) n = lua_rawlen(L, index); char** my_ptrptr = malloc((n+2)*sizeof(char*)); if(!my_ptrptr) return NULL; my_ptrptr[n+1] = NULL; my_ptrptr[0] = strdup(script); if(!my_ptrptr[0]) { free(my_ptrptr); return NULL; } int i; for(i = 1; i <= n; ++i) { lua_pushinteger(L, i); lua_gettable(L, index); my_ptrptr[i] = strdup(luaL_checkstring(L, -1)); lua_pop(L, 1); if(!my_ptrptr[i]) { i--; for(; i >= 0; --i) free(my_ptrptr[i]); free(my_ptrptr); return NULL; } } return my_ptrptr; } static char** table_to_evp(lua_State *L, int index) { if(!lua_istable(L, index)) return NULL; size_t n = lua_rawlen(L, index); char** my_ptrptr = malloc((n+1)*sizeof(char*)); if(!my_ptrptr) return NULL; int i; for(i = 0; i < n; ++i) { lua_pushinteger(L, i+1); lua_gettable(L, index); my_ptrptr[i] = strdup(luaL_checkstring(L, -1)); lua_pop(L, 1); if(!my_ptrptr[i]) { i--; for(; i >= 0; --i) free(my_ptrptr[i]); free(my_ptrptr); return NULL; } } my_ptrptr[n] = NULL; return my_ptrptr; } void free_ptrptr(char** ptrptr) { if(!ptrptr) return; int i; for(i = 0; ptrptr[i]; ++i) free(ptrptr[i]); free(ptrptr); } static void child_add(lua_State *L, pid_t pid, int fd) { lua_getglobal(L, LUA_UTILLIBNAME); lua_getfield(L, -1, LUA_UTILCHLIDRENNAME); lua_remove(L, -2); lua_newtable(L); lua_pushinteger(L, pid); lua_setfield(L, -2, "pid"); lua_pushinteger(L, fd); lua_setfield(L, -2, "errfd"); lua_pushinteger(L, pid); lua_pushvalue(L, -2); lua_settable(L, -4); lua_remove(L, -2); } static void child_remove(lua_State *L, pid_t pid) { lua_getglobal(L, LUA_UTILLIBNAME); lua_getfield(L, -1, LUA_UTILCHLIDRENNAME); lua_pushinteger(L, pid); lua_pushnil(L); lua_settable(L, -3); lua_pop(L, 2); } static int child_find(lua_State *L, pid_t pid) { lua_getglobal(L, LUA_UTILLIBNAME); lua_getfield(L, -1, LUA_UTILCHLIDRENNAME); lua_pushinteger(L, pid); lua_gettable(L, -2); if(lua_isnil(L, -1)) { lua_pop(L, 3); return -1; } lua_getfield(L, -1, "errfd"); int fd = luaL_checkint(L, -1); lua_pop(L, 1); lua_remove(L, -2); lua_remove(L, -3); return fd; } static void prepare_fds(lua_State *L, int *child_stdio, int pipefd) { int fd; for (fd=getdtablesize();fd>=0;--fd) // close all file descriptors if(fd != pipefd && fd != child_stdio[0] && fd != child_stdio[1] && fd != child_stdio[2]) close(fd); fcntl(pipefd, F_SETFD, FD_CLOEXEC); int i, ret; for(i = 0; i<3; ++i) { if(child_stdio[i] >= 0) { fd = dup2(child_stdio[i], i); if(fd == -1) { ret = write(pipefd, (void*)(&errno), sizeof(errno)); lua_close(L); exit(ret); } close(child_stdio[i]); } } } static int l_util_exec(lua_State *L) { const char* script = luaL_checkstring(L, 1); int child_stdio[3]; child_stdio[0] = luaL_optint(L, 4, -1); child_stdio[1] = luaL_optint(L, 5, -1); child_stdio[2] = luaL_optint(L, 6, -1); int pipefd[2]; if(pipe(pipefd) == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 2; } pid_t pid; pid = fork(); if(pid == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 2; } if(!pid) { prepare_fds(L, child_stdio, pipefd[1]); char** argv = table_to_argv(L, script, 2); char** evp = table_to_evp(L, 3); if(!argv) { int err = ENOMEM; int ret = write(pipefd[1], (void*)(&err), sizeof(err)); free_ptrptr(evp); lua_close(L); exit(ret); } execve(script, argv, evp); int ret = write(pipefd[1], (void*)(&errno), sizeof(errno)); free_ptrptr(argv); free_ptrptr(evp); lua_close(L); exit(ret); } close(pipefd[1]); child_add(L, pid, pipefd[0]); return 1; } static int l_util_waitpid(lua_State *L) { int status = 0; pid_t pid = waitpid(-1, &status, WNOHANG); if(!pid || (pid < 0 && (errno == ECHILD))) { lua_pushnil(L); return 1; } if(pid < 0) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 2; } int errfd = child_find(L, pid); if(errfd != -1) { child_remove(L, pid); int err = 0; if(read(errfd, (void*)(&err), sizeof(err)) >= sizeof(err)) { lua_pushliteral(L, "error"); lua_pushstring(L, strerror(err)); // FIXXXXXME: strerror is not threadsafe!!! close(errfd); return 3; } close(errfd); } else { lua_pushnil(L); lua_pushliteral(L, "unknown child pid returned"); return 2; } if(WIFEXITED(status)) { lua_pushliteral(L, "exit"); lua_pushinteger(L, WEXITSTATUS(status)); } else if(WIFSIGNALED(status)) { lua_pushliteral(L, "signal"); lua_pushinteger(L, WTERMSIG(status)); } else { lua_pushliteral(L, "unknown"); lua_pushnil(L); } return 3; } static int l_util_pipe(lua_State *L) { int pipefd[2]; if(pipe(pipefd) == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 2; } lua_pushinteger(L, pipefd[0]); lua_pushinteger(L, pipefd[1]); return 2; } static int l_util_kill(lua_State *L) { int pid = luaL_checkint(L, 1); int sig = SIGTERM; if(lua_isnumber(L, 2)) { sig = luaL_checkint(L, 2); } else { const char* signame = luaL_optstring(L, 2, "TERM"); if(!strcasecmp(signame, "HUP")) sig = SIGHUP; else if(!strcasecmp(signame, "INT")) sig = SIGINT; else if(!strcasecmp(signame, "QUIT")) sig = SIGQUIT; else if(!strcasecmp(signame, "ILL")) sig = SIGILL; else if(!strcasecmp(signame, "TRAP")) sig = SIGTRAP; else if(!strcasecmp(signame, "ABRT")) sig = SIGABRT; else if(!strcasecmp(signame, "IOT")) sig = SIGIOT; else if(!strcasecmp(signame, "BUS")) sig = SIGBUS; else if(!strcasecmp(signame, "FPE")) sig = SIGFPE; else if(!strcasecmp(signame, "KILL")) sig = SIGKILL; else if(!strcasecmp(signame, "USR1")) sig = SIGUSR1; else if(!strcasecmp(signame, "SEGV")) sig = SIGSEGV; else if(!strcasecmp(signame, "USR2")) sig = SIGUSR2; else if(!strcasecmp(signame, "PIPE")) sig = SIGPIPE; else if(!strcasecmp(signame, "ALRM")) sig = SIGALRM; else if(!strcasecmp(signame, "TERM")) sig = SIGTERM; else if(!strcasecmp(signame, "STKFLT")) sig = SIGSTKFLT; else if(!strcasecmp(signame, "CLD")) sig = SIGCLD; else if(!strcasecmp(signame, "CHLD")) sig = SIGCHLD; else if(!strcasecmp(signame, "CONT")) sig = SIGCONT; else if(!strcasecmp(signame, "STOP")) sig = SIGSTOP; else if(!strcasecmp(signame, "TSTP")) sig = SIGTSTP; else if(!strcasecmp(signame, "TTIN")) sig = SIGTTIN; else if(!strcasecmp(signame, "TTOU")) sig = SIGTTOU; else if(!strcasecmp(signame, "URG")) sig = SIGURG; else if(!strcasecmp(signame, "XCPU")) sig = SIGXCPU; else if(!strcasecmp(signame, "XFSZ")) sig = SIGXFSZ; else if(!strcasecmp(signame, "VTALRM")) sig = SIGVTALRM; else if(!strcasecmp(signame, "PROF")) sig = SIGPROF; else if(!strcasecmp(signame, "WINCH")) sig = SIGWINCH; else if(!strcasecmp(signame, "POLL")) sig = SIGPOLL; else if(!strcasecmp(signame, "IO")) sig = SIGIO; else if(!strcasecmp(signame, "PWR")) sig = SIGPWR; else if(!strcasecmp(signame, "SYS")) sig = SIGSYS; else if(!strcasecmp(signame, "UNUSED")) sig = SIGUNUSED; else { lua_pushnil(L); lua_pushliteral(L, "unknown signal"); return 2; } } if(kill(pid, sig) == -1) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); // FIXXXXXME: strerror is not threadsafe!!! return 2; } lua_pushboolean(L, 1); return 1; } static const struct luaL_Reg util_funcs [] = { { "select", l_util_select }, { "exec", l_util_exec }, { "waitpid", l_util_waitpid }, { "pipe", l_util_pipe }, { "kill", l_util_kill }, { NULL, NULL } }; LUALIB_API int luaopen_util(lua_State *L) { #if LUA_VERSION_NUM > 501 lua_newtable(L); luaL_setfuncs(L, util_funcs, 0); lua_pushvalue(L, -1); lua_setglobal(L, LUA_UTILLIBNAME); #else luaL_register(L, LUA_UTILLIBNAME, util_funcs); #endif lua_newtable(L); lua_setfield(L, -2, LUA_UTILCHLIDRENNAME); return 1; }