/* lpty_hasproc * * Check wether this pty has an active process attached to it. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1 true if there is an active subprocess, false if not. */ static int lpty_hasproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); int hasit = _lpty_hasrunningchild(pty); lua_pushboolean(L, hasit); return 1; }
/* lpty_exitstatus * * Tries to find the exit status of the last process running in a pty. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1, +2 false, nil if there is an active subprocess * 'exit', code if the previous subprocess exited via exit * 'sig', signum if the previous subprocess was terminated by a signal * 'unk', 0 if we have no information about the previous subprocess */ static int lpty_exitstatus(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); if (!_lpty_hasrunningchild(pty) && pty->child != -1) { int i; for (i = 0; i < EXITSTATUS_BUFSIZ; ++i) { if (_lpty_exitstatus_buffer.ecodes[i].child == pty->child) { int status = _lpty_exitstatus_buffer.ecodes[i].status; if (WIFEXITED(status)) { lua_pushliteral(L, "exit"); lua_pushinteger(L, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { lua_pushliteral(L, "sig"); lua_pushinteger(L, WTERMSIG(status)); } break; } } if (i == EXITSTATUS_BUFSIZ) { lua_pushliteral(L, "unk"); lua_pushinteger(L, 0); } } else { lua_pushboolean(L, 0); lua_pushnil(L); } return 2; }
/* lpty_gc * * __gc metamethod for the pty userdata. * Kills child process, closes all fd's. * * Arguments: * L Lua State * * Lua Stack: * 1 pty object */ static int lpty__gc(lua_State *L) { lPty *pty = lpty_toLPty(L, 1); /* if here is a running child process, dispose of it. * We do the waitpid() here because this might occur on program shutdown * and thus the SIGCHLD might be delivered after we've terminated. WNOHANG * ensures that we don't bother waiting for a dead child that has already * been reaped, and keeping the signal handler active ensures that no dying * child is left behind. */ if (_lpty_hasrunningchild(pty)) { /* no need to be gentle, this process has been abandoned. */ kill(pty->child, SIGKILL); waitpid(pty->child, NULL, WNOHANG); } if (pty->m_fd > 0) close(pty->m_fd); if (pty->s_fd > 0) close(pty->s_fd); return 0; }
/* lpty_endproc * * kill the process corresponding to our pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) flag wether to send SIGTERM (default, false) or SIGKILL (true) * * Lua Returns: * - * * Note: * No error is generated if the child process was not active any more. */ static int lpty_endproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); int sigkill = 0; if (lua_gettop(L) > 1) { luaL_checktype(L, 2, LUA_TBOOLEAN); sigkill = lua_toboolean(L, 2); } if (_lpty_hasrunningchild(pty)) { if (sigkill) kill(pty->child, SIGKILL); else kill(pty->child, SIGTERM); } return 0; }
/* lpty_startproc * * start a process with our pty as its controlling terminal. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1 false if the re was already an active subprocess of this pty, true if * not and we started one. * * Note: * We can not determine wether the command to be spawned in the child process * was successful. */ static int lpty_startproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *cmd = luaL_checkstring(L, 2); /* if pty already has an active child process we just return false and do * nothing */ if (_lpty_hasrunningchild(pty)) lua_pushboolean(L, 0); else { pid_t child; int ttyfd = pty->s_fd; signal(SIGCHLD, _lpty_sigchld_handler); /* now start child process */ child = fork(); if (child == 0) { /* child process */ /* fill execvp args array from function arguments */ int nargs = lua_gettop(L) - 1; const char **args = calloc(nargs + 1, sizeof(char*)); int i; args[0] = cmd; for (i = 1; i < nargs; ++i) args[i] = lua_tostring(L, 2 + i); args[nargs] = NULL; /* prepare child processes standard file handles */ dup2(ttyfd, 0); dup2(ttyfd, 1); dup2(ttyfd, 2); /* need to create new session id for slave in order for the tty to * become a controlling tty */ if (setsid() < (pid_t)0) { fprintf(stderr, "lpty failed to create new session id."); exit(EXIT_FAILURE); /* we need to terminate here! */ } /* reset SIGCHLD handler then start our process */ signal(SIGCHLD, SIG_DFL); execvp(cmd, (char* const*)args); /* if we ever get here, an error has occurred. * Note: this error will only be visible as output to the pty from the parent side! */ free(args); fprintf(stderr, "error: lpty failed to start child process: %s", strerror(errno)); exit(EXIT_FAILURE); /* we need to terminate here! */ } else if (child > 0) { /* parent process: clean up, store child pid, return success */ pty->child = child; lua_pushboolean(L, 1); } else { return lpty_error(L, pty->flags.throwerrors, "lpty failed to create child process: %s", strerror(errno)); } } return 1; }