[asterisk-commits] mnicholson: branch mnicholson/asttest r250728 - in /team/mnicholson/asttest/a...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Mar 4 13:52:11 CST 2010


Author: mnicholson
Date: Thu Mar  4 13:52:07 2010
New Revision: 250728

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=250728
Log:
Added an exec_io() function to proclib (proc.exec_io).  This works similar to popen but allows you to read from stdout and stderr and write to stdin.  A self-test testing this functionality was also added.

Added:
    team/mnicholson/asttest/asttest/self-tests/proclib_io/
    team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua   (with props)
Modified:
    team/mnicholson/asttest/asttest/lua/proclib.c

Modified: team/mnicholson/asttest/asttest/lua/proclib.c
URL: http://svnview.digium.com/svn/asterisk/team/mnicholson/asttest/asttest/lua/proclib.c?view=diff&rev=250728&r1=250727&r2=250728
==============================================================================
--- team/mnicholson/asttest/asttest/lua/proclib.c (original)
+++ team/mnicholson/asttest/asttest/lua/proclib.c Thu Mar  4 13:52:07 2010
@@ -34,26 +34,33 @@
 #include <sys/time.h>
 #include <time.h>
 
-/*!
- * \brief [lua_CFunction proc.exec] Start a process.
- * \param L the lua state to use
- *
- * This function forks and execs the given process.
- */
-static int exec_proc(lua_State *L) {
-	pid_t pid;
-	pid_t *p;
+static FILE *create_iolib_file(lua_State *L, const char *name, int fd, const char *mode) {
+	FILE **file;
+
+	lua_pushstring(L, name);
+	file = lua_newuserdata(L, sizeof(FILE *));
+	*file = fdopen(fd, mode);
+	luaL_getmetatable(L, LUA_FILEHANDLE);
+	lua_setmetatable(L, -2);
+	lua_rawset(L, -3);
+	return *file;
+}
+
+/*!
+ * \brief Create and array with the function arguments suitable to pass to
+ * execvp.
+ * \param L the lua state to use
+ */
+static const char **build_argv(lua_State *L, const char *name) {
 	int args = lua_gettop(L);
 	const char **argv;
-	const char *name = luaL_checkstring(L, 1);
 	size_t argc;
-	int i, fd;
-
-	/* build the argument list */
+	int i;
+
 	argc = args + 1; /* the number of args plus 1 for the terminating NULL */
 	if (!(argv = malloc(argc * sizeof(char *)))) {
 		lua_pushliteral(L, "error allocating memory while spawning process");
-		goto e_return;
+		lua_error(L); /* never returns */
 	}
 
 	argv[0] = name;
@@ -61,19 +68,58 @@
 
 	for (i = 2; i <= args; i++) {
 		argv[i - 1] = lua_tostring(L, i);
+	}
+
+	return argv;
+}
+
+/*!
+ * \brief [lua_CFunction proc.exec_io] Start a process.
+ * \param L the lua state to use
+ *
+ * This function forks and execs the given process with support for reading
+ * from and writing to stdio file descriptors.
+ */
+static int exec_proc_with_stdio(lua_State *L) {
+	pid_t pid;
+	pid_t *p;
+	const char *name = luaL_checkstring(L, 1);
+	const char **argv = build_argv(L, name);
+	int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
+
+	if (pipe(stdin_pipe)) {
+		lua_pushliteral(L, "error creating stdin pipe: ");
+		lua_pushstring(L, strerror(errno));
+		lua_concat(L, 2);
+		goto e_free_argv;
+	}
+
+	if (pipe(stdout_pipe)) {
+		lua_pushliteral(L, "error creating stdout pipe: ");
+		lua_pushstring(L, strerror(errno));
+		lua_concat(L, 2);
+		goto e_close_stdin_pipe;
+	}
+
+	if (pipe(stderr_pipe)) {
+		lua_pushliteral(L, "error creating stderr pipe: ");
+		lua_pushstring(L, strerror(errno));
+		lua_concat(L, 2);
+		goto e_close_stdout_pipe;
 	}
 
 	/* start the process */
 	pid = fork();
 	if (pid == 0) {
-		/* open dev null and use it for stdin, stdout, and stderr */
-		/* XXX in the future, we should create pipes for stdin, stdout,
-		 * and stderr so that our lua scrips can interact with them */
-		fd = open("/dev/null", O_RDWR);
-		/* XXX check for error?!! */
-		dup2(fd, STDIN_FILENO);
-		dup2(fd, STDOUT_FILENO);
-		dup2(fd, STDERR_FILENO);
+		/* replace stdin, stdout, and stderr with our pipes */
+		dup2(stdin_pipe[0], STDIN_FILENO);
+		dup2(stdout_pipe[1], STDOUT_FILENO);
+		dup2(stderr_pipe[1], STDERR_FILENO);
+
+		/* close the halves of the pipes that we don't need */
+		close(stdin_pipe[1]);
+		close(stdout_pipe[0]);
+		close(stderr_pipe[0]);
 
 		execvp(name, (char * const *) argv);
 		exit(1);
@@ -81,14 +127,25 @@
 		lua_pushliteral(L, "error spawning process (fork error): ");
 		lua_pushstring(L, strerror(errno));
 		lua_concat(L, 2);
-		goto e_free_argv;
+		goto e_close_stderr_pipe;
 	}
 
 	free(argv);
+
+	/* close the halves of the pipes that we don't need */
+	close(stdin_pipe[0]);
+	close(stdout_pipe[1]);
+	close(stderr_pipe[1]);
 
 	lua_newtable(L);
 	luaL_getmetatable(L, "proclib_proc");
 	lua_setmetatable(L, -2);
+
+	/* now create lua FILE* objects from our file descriptors */
+	create_iolib_file(L, "stdin", stdin_pipe[1], "w");
+	create_iolib_file(L, "stdout", stdout_pipe[0], "r");
+	create_iolib_file(L, "stderr", stderr_pipe[0], "r");
+
 
 	/* store the pid */
 	lua_pushliteral(L, "pid");
@@ -100,14 +157,79 @@
 
 	return 1;
 
+e_close_stderr_pipe:
+	close(stderr_pipe[0]);
+	close(stderr_pipe[1]);
+
+e_close_stdout_pipe:
+	close(stdout_pipe[0]);
+	close(stdout_pipe[1]);
+
+e_close_stdin_pipe:
+	close(stdin_pipe[0]);
+	close(stdin_pipe[1]);
+
 e_free_argv:
 	free(argv);
 
-e_return:
 	/* error string should already be on the stack */
 	return lua_error(L);
 }
 
+/*!
+ * \brief [lua_CFunction proc.exec] Start a process.
+ * \param L the lua state to use
+ *
+ * This function forks and execs the given process.
+ */
+static int exec_proc(lua_State *L) {
+	pid_t pid;
+	pid_t *p;
+	const char *name = luaL_checkstring(L, 1);
+	const char **argv = build_argv(L, name);
+	int fd;
+
+	/* start the process */
+	pid = fork();
+	if (pid == 0) {
+		/* open dev null and use it for stdin, stdout, and stderr */
+		fd = open("/dev/null", O_RDWR);
+		/* XXX check for error?!! */
+		dup2(fd, STDIN_FILENO);
+		dup2(fd, STDOUT_FILENO);
+		dup2(fd, STDERR_FILENO);
+
+		execvp(name, (char * const *) argv);
+		exit(1);
+	} else if (pid == -1) {
+		lua_pushliteral(L, "error spawning process (fork error): ");
+		lua_pushstring(L, strerror(errno));
+		lua_concat(L, 2);
+		goto e_free_argv;
+	}
+
+	free(argv);
+
+	lua_newtable(L);
+	luaL_getmetatable(L, "proclib_proc");
+	lua_setmetatable(L, -2);
+
+	/* store the pid */
+	lua_pushliteral(L, "pid");
+	p = lua_newuserdata(L, sizeof(pid_t));
+	*p = pid;
+	luaL_getmetatable(L, "proclib_pid");
+	lua_setmetatable(L, -2);
+	lua_rawset(L, -3);
+
+	return 1;
+
+e_free_argv:
+	free(argv);
+
+	/* error string should already be on the stack */
+	return lua_error(L);
+}
 /*!
  * \brief [lua_CFunction pid:__gc] Kill the process with the given pid.
  * \param L the lua state to use
@@ -328,6 +450,7 @@
 
 static luaL_Reg proclib[] = {
 	{"exec", exec_proc},
+	{"exec_io", exec_proc_with_stdio},
 	{NULL, NULL},
 };
 
@@ -343,7 +466,41 @@
 	{NULL, NULL},
 };
 
+/* copied from liolib.c in the lua source */
+static int pushresult (lua_State *L, int i, const char *filename) {
+	int en = errno;  /* calls to Lua API may change this value */
+	if (i) {
+		lua_pushboolean(L, 1);
+		return 1;
+	} else {
+		lua_pushnil(L);
+		if (filename)
+			lua_pushfstring(L, "%s: %s", filename, strerror(en));
+		else
+			lua_pushfstring(L, "%s", strerror(en));
+		lua_pushinteger(L, en);
+		return 3;
+	}
+}
+
+/* adapted from io_fclose in liolib.c in the lua source */
+static int io_fclose (lua_State *L) {
+	FILE **p = luaL_checkudata(L, 1, LUA_FILEHANDLE);
+	int ok = (fclose(*p) == 0);
+	*p = NULL;
+	return pushresult(L, ok, NULL);
+}
+
 int luaopen_proclib(lua_State *L) {
+	/* set up a private environment containing a __close function that is
+	 * necessary for using the LUA_FILEHANDLE metatable.  This metatable is
+	 * used by the proc.exec_io function to manage pipes for stdio.
+	 */
+	lua_createtable(L, 0, 1);
+	lua_replace(L, LUA_ENVIRONINDEX);
+	lua_pushcfunction(L, io_fclose);
+	lua_setfield(L, LUA_ENVIRONINDEX, "__close");
+
 	/* register our functions */
 	luaL_register(L, "proc", proclib);
 

Added: team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua
URL: http://svnview.digium.com/svn/asterisk/team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua?view=auto&rev=250728
==============================================================================
--- team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua (added)
+++ team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua Thu Mar  4 13:52:07 2010
@@ -1,0 +1,17 @@
+-- test proclib's ablity to read from stdout and stderr and write to stdin
+
+test_string = "this is a test"
+p = proc.exec_io("echo", "-n", test_string)
+res = p.stdout:read("*a")
+
+fail_if(test_string ~= res, "echo test failed: read '" .. res .. "' expected '" .. test_string .. "'") 
+
+
+p = proc.exec_io("cat")
+p.stdin:write(test_string)
+p.stdin:close()
+
+res = p.stdout:read("*a")
+
+fail_if(test_string ~= res, "cat test failed: read '" .. res .. "' expected '" .. test_string .. "'") 
+

Propchange: team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mnicholson/asttest/asttest/self-tests/proclib_io/test.lua
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list