/* 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_flush * * flush data from pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) mode, what to flush * * Lua Returns: * nothing */ static int lpty_flush(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *mode = luaL_optstring(L, 2, NULL); int which = TCIOFLUSH; if (mode) { int l = strlen(mode); if (l == 1) { switch (*mode) { case 'i': case 'I': which = TCIFLUSH; break; case 'o': case 'O': which = TCOFLUSH; break; } } } tcflush(pty->m_fd, which); if (which != TCOFLUSH && pty->e_mfd > -1) tcflush(pty->e_mfd, which); return 0; }
/* 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_setflag * * change the creation flags of the pty at runtime * * Arguments: * L Lua state * * Lua stack: 1 lpty userdata * * Lua returns: * +1 a table containing flags and values, as specified for lpty.new() */ static int lpty_setflag(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char* flag = luaL_checkstring(L, 2); int val = lua_toboolean(L, 3); int tty_flags_changed = 0; if (!strcmp(flag, "throw_errors")) { pty->flags.throwerrors = val; } else if (!strcmp(flag, "no_local_echo")) { pty->flags.nolocalecho = val; tty_flags_changed = 1; } else if (!strcmp(flag, "raw_mode")) { pty->flags.rawmode = val; tty_flags_changed = 1; } else if (!strcmp(flag, "use_path")) { pty->flags.usepath = val; } else if (!strcmp(flag, "separate_stderr")) { _lpty_separate_stderr(pty, val); } else { return _lpty_error(L, pty->flags.throwerrors, "unknown flag: %s", flag); } if (tty_flags_changed) { tcsetattr(pty->s_fd, TCSANOW, &pty->otios); if (pty->flags.nolocalecho) _lpty_tsetnoecho(pty); if (pty->flags.rawmode) _lpty_tsetraw(pty); } lua_pushboolean(L, 1); return 1; }
/* lpty_readerr * * ... */ static int lpty_readerr(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, 0); if (pty->e_mfd == -1) { lua_pushnil(L); return 1; } int ok = _lpty_select(pty->e_mfd, -1, timeo); if (ok > 0) { char buf[READER_BUFSIZ]; // should probably be more flexible int readn = read(pty->e_mfd, buf, READER_BUFSIZ); if (readn > 0) { lua_pushlstring(L, buf, readn); } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 1; }
/* lpty_readline * * read a line from the master side of a pty. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 the data read from the master side of the pty, or nil if the read timed * out * * Note: * you also read back the stuff written to the pty with lpty_write() below! */ static int lpty_readline(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); int wantnl = _lpty_optboolean(L, 2, 0); double timeo = (double)luaL_optnumber(L, 3, -1); char buf[READER_BUFSIZ]; // should probably be more flexible int rd = 0; int ok = 1, isline = 0; double start = _lpty_gettime(); double tmo = timeo; if (start < 0) return _lpty_error(L, pty->flags.throwerrors, "lpty readline failed: (%d) %s", errno, strerror(errno)); if (timeo < 0) { tmo = 0; ok = _lpty_waitfordata(pty, (2^32)-1, 0); } do { ok = _lpty_waitfordata(pty, tmo, 0); if (ok > 0) { if (read(pty->m_fd, buf + rd, 1) > 0) { if (buf[rd] == '\n') isline = 1; ++rd; } else { ok = 0; } } if (!isline && ok && timeo > 0) { double now = _lpty_gettime(); if (now < 0) return _lpty_error(L, pty->flags.throwerrors, "lpty readline failed: (%d) %s", errno, strerror(errno)); if (now - timeo >= start) isline = 1; else { tmo = timeo + start - now; if (tmo < 0) tmo = 0; ok = 1; } } } while (rd < READER_BUFSIZ && !isline && ok); if (rd > 0) { if (!wantnl && buf[rd-1] == '\n') --rd; if (!wantnl && buf[rd-1] == '\r') --rd; buf[rd] = 0; lua_pushstring(L, buf); /* we don't consider EINTR and ECHILD errors */ } else if (errno && (errno != EINTR) && (errno != ECHILD)) return _lpty_error(L, pty->flags.throwerrors, "lpty readline failed: (%d) %s", errno, strerror(errno)); else lua_pushnil(L); return 1; }
/* lpty_sendok * * Check wether the master side of this pty can accept data from us * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 true if pty can accept data, false if not. */ static int lpty_sendok(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, 0); int ok = _lpty_waitfordata(pty, timeo, 1); lua_pushboolean(L, ok > 0); return 1; }
/* lpty_ttyname * * return the name of the slave side of the pty * * Arguments: * L Lua State * * Lua Stack: * lpty userdata * * Lua Returns: * +1 the name of the terminal on the slave side of this pty */ static int lpty_ttyname(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); char *name = ptsname(pty->m_fd); if (name) lua_pushstring(L, name); else return lpty_error(L, pty->flags.throwerrors, "lpty could not fetch slave tty name: %s", strerror(errno)); return 1; }
/* lpty_toString * * __tostring metamethod for the lpty userdata. * Returns a string representation of the lpty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata */ static int lpty__toString(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); char buf[TOSTRING_BUFSIZ]; /* length of type name + length of hex pointer rep + '0x' + ' ()' + '\0' */ if (strlen(LPTY) + (sizeof(void*) * 2) + 2 + 4 > TOSTRING_BUFSIZ) return luaL_error(L, "Whoopsie... the string representation seems to be too long."); /* this should not happen, just to be sure! */ sprintf(buf, "%s (%p)", LPTY, pty); lua_pushstring(L, buf); return 1; }
/* 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_expect * * reads lines from the input until a pattern is matched. The actual * function is written in lua and available as the first upvalue to this * function. See luaopen_lpty() below. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 pattern to look for * 3 (opt) boolean whether to match plainly (true) or pattern (false, default) * 4 (opt) timeout in seconds * * Lua Returns: * +1... matches */ static int lpty_expect(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); (void) luaL_checkstring(L, 2); (void) _lpty_optboolean(L, 3, 0); (void) luaL_optnumber(L, 4, 0); int nargs = lua_gettop(L); lua_pushvalue(L, lua_upvalueindex(1)); lua_pushvalue(L, 1); lua_pushvalue(L, 2); if (nargs > 2) lua_pushvalue(L, 3); if (nargs > 3) lua_pushvalue(L, 4); if (lua_pcall(L, nargs, LUA_MULTRET, 0) != LUA_OK) { const char* err = lua_tostring(L, -1); _lpty_error(L, pty->flags.throwerrors, err); } return lua_gettop(L) - nargs; }
/* lpty_send * * write data to the master side of a pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 data to write * 3 (optional) timeout in seconds * * Lua Returns: * +1 the amount of bytes actually written, or nil if the write attempt timed * out */ static int lpty_send(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *data = luaL_checkstring(L, 2); double timeo = (double)luaL_optnumber(L, 3, -1); int written = -1; int ok = 1; if (timeo >= 0) ok = _lpty_waitfordata(pty, timeo, 1); if (ok > 0) written = write(pty->m_fd, data, strlen(data)); if (written >= 0) lua_pushinteger(L, written); else if (errno && (errno != EINTR)) return lpty_error(L, pty->flags.throwerrors, "lpty send failed: (%d) %s", errno, strerror(errno)); else lua_pushnil(L); return 1; }
/* lpty_read * * read data from the master side of a pty. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 the data read from the master side of the pty, or nil if the read timed * out * * Note: * you also read back the stuff written to the pty with lpty_write() below! */ static int lpty_read(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, -1); char buf[READER_BUFSIZ]; // should probably be more flexible int readn = -1; int ok = 1; if (timeo >= 0) ok = _lpty_waitfordata(pty, timeo, 0); if (ok > 0) readn = read(pty->m_fd, buf, READER_BUFSIZ); if (readn >= 0) { buf[readn] = 0; lua_pushstring(L, buf); /* we don't consider EINTR and ECHILD errors */ } else if (errno && (errno != EINTR) && (errno != ECHILD)) return lpty_error(L, pty->flags.throwerrors, "lpty read failed: (%d) %s", errno, strerror(errno)); else lua_pushnil(L); return 1; }
/* lpty_getflags * * Get the creation flags of the pty * * Arguments: * L Lua state * * Lua stack: 1 lpty userdata * * Lua returns: * +1 a table containing flags and values, as specified for lpty.new() */ static int lpty_getflags(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); lua_newtable(L); lua_pushliteral(L, "throw_errors"); lua_pushboolean(L, pty->flags.throwerrors); lua_rawset(L, -3); lua_pushliteral(L, "no_local_echo"); lua_pushboolean(L, pty->flags.nolocalecho); lua_rawset(L, -3); lua_pushliteral(L, "raw_mode"); lua_pushboolean(L, pty->flags.rawmode); lua_rawset(L, -3); lua_pushliteral(L, "use_path"); lua_pushboolean(L, pty->flags.usepath); lua_rawset(L, -3); lua_pushliteral(L, "separate_stderr"); lua_pushboolean(L, pty->e_mfd != -1); lua_rawset(L, -3); return 1; }
/* 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; }
/* lpty_geterrfd * * ... */ static int lpty_geterrfd(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); lua_pushinteger(L, pty->e_mfd); return 1; }