[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