コード例 #1
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #2
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #3
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #4
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #5
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #6
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #7
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #8
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #9
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #10
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #11
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #12
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #13
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #14
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* 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;
}
コード例 #15
0
ファイル: lpty.c プロジェクト: jgaskins/brat
/* 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;
}
コード例 #16
0
ファイル: lpty.c プロジェクト: yingnierxiao/lpty
/* lpty_geterrfd
 *
 * ...
 */
static int lpty_geterrfd(lua_State *L)
{
	lPty *pty = lpty_checkLPty(L, 1);
	lua_pushinteger(L, pty->e_mfd);
	return 1;
}