summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Renner Berthing <esmil@mailme.dk>2013-01-29 18:31:31 +0100
committerEmil Renner Berthing <esmil@mailme.dk>2013-01-30 22:11:14 +0100
commit501d84381c8603e5680b86d65afeb633279116f9 (patch)
tree861c48a90e73c31c4759f0ee464ee81c02b3907e
parent11d341652792bf1eb21b5f42a4a0a5ea5298b164 (diff)
downloadlem-501d84381c8603e5680b86d65afeb633279116f9.tar.gz
lem-501d84381c8603e5680b86d65afeb633279116f9.tar.xz
lem-501d84381c8603e5680b86d65afeb633279116f9.zip
io: rework io.popen to use posix_spawn()
..and accept 'rw' flag to make reading _and_ writing to the process' stdin and stdout possible.
-rw-r--r--lem/io/core.c71
-rw-r--r--lem/io/stream.c60
-rwxr-xr-xtest/popen.lua40
3 files changed, 110 insertions, 61 deletions
diff --git a/lem/io/core.c b/lem/io/core.c
index c56463c..b5fe4d6 100644
--- a/lem/io/core.c
+++ b/lem/io/core.c
@@ -30,11 +30,13 @@
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
+#include <spawn.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
#include <sys/un.h>
#include <sys/ucred.h>
#include <netinet/in.h>
+extern char **environ;
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 104
#endif
@@ -107,6 +109,9 @@ error:
#include "tcp.c"
#include "unix.c"
+/*
+ * io.open()
+ */
struct open {
struct lem_async a;
const char *path;
@@ -249,6 +254,70 @@ io_open(lua_State *T)
return lua_yield(T, 3);
}
+/*
+ * io.popen()
+ */
+static const char *const io_popen_modes[] = { "r", "w", "rw", NULL };
+
+static int
+io_popen(lua_State *T)
+{
+ const char *cmd = luaL_checkstring(T, 1);
+ int mode = luaL_checkoption(T, 2, "r", io_popen_modes);
+ char *const argv[4] = { "/bin/sh", "-c", (char *)cmd, NULL };
+ posix_spawn_file_actions_t fa;
+ int fd[2];
+ int err;
+
+ posix_spawn_file_actions_init(&fa);
+ switch (mode) {
+ case 0: /* "r" */
+ if (pipe(fd))
+ return io_strerror(T, errno);
+ posix_spawn_file_actions_adddup2(&fa, fd[1], 1);
+ posix_spawn_file_actions_addclose(&fa, fd[1]);
+ break;
+ case 1: /* "w" */
+ if (pipe(fd))
+ return io_strerror(T, errno);
+ err = fd[0];
+ fd[0] = fd[1];
+ fd[1] = err;
+ posix_spawn_file_actions_adddup2(&fa, fd[1], 0);
+ posix_spawn_file_actions_addclose(&fa, fd[1]);
+ break;
+ case 2: /* "rw" */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd))
+ return io_strerror(T, errno);
+ posix_spawn_file_actions_adddup2(&fa, fd[1], 0);
+ posix_spawn_file_actions_adddup2(&fa, fd[1], 1);
+ posix_spawn_file_actions_addclose(&fa, fd[1]);
+ break;
+ }
+
+ /* set our socket flags */
+ if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) == -1 ||
+ fcntl(fd[0], F_SETFL, O_NONBLOCK) == -1) {
+ err = errno;
+ goto error;
+ }
+
+ err = posix_spawn(NULL, argv[0], &fa, NULL, argv, environ);
+ lem_debug("err = %d, %s", err, strerror(err));
+ if (err)
+ goto error;
+
+ posix_spawn_file_actions_destroy(&fa);
+ close(fd[1]);
+ stream_new(T, fd[0], lua_upvalueindex(1));
+ return 1;
+error:
+ posix_spawn_file_actions_destroy(&fa);
+ close(fd[0]);
+ close(fd[1]);
+ return io_strerror(T, err);
+}
+
static void
push_stdstream(lua_State *L, int fd)
{
@@ -384,7 +453,7 @@ luaopen_lem_io_core(lua_State *L)
lua_setfield(L, -2, "open");
/* insert popen function */
lua_getfield(L, -1, "Stream"); /* upvalue 1 = Stream */
- lua_pushcclosure(L, stream_popen, 1);
+ lua_pushcclosure(L, io_popen, 1);
lua_setfield(L, -2, "popen");
/* create tcp table */
diff --git a/lem/io/stream.c b/lem/io/stream.c
index 204df81..0056233 100644
--- a/lem/io/stream.c
+++ b/lem/io/stream.c
@@ -530,63 +530,3 @@ stream_sendfile(lua_State *T)
lua_settop(T, 2);
return lua_yield(T, 2);
}
-
-static int
-stream_popen(lua_State *T)
-{
- const char *cmd = luaL_checkstring(T, 1);
- const char *mode = luaL_optstring(T, 2, "r");
- int fd[2];
- int err;
-
- if (mode[0] != 'r' && mode[0] != 'w')
- return luaL_error(T, "invalid mode string");
-
- if (pipe(fd))
- return io_strerror(T, errno);
-
- switch (fork()) {
- case -1: /* error */
- err = errno;
- close(fd[0]);
- close(fd[1]);
- return io_strerror(T, err);
-
- case 0: /* child */
- if (mode[0] == 'r') {
- close(fd[0]);
- dup2(fd[1], 1);
- } else {
- close(fd[1]);
- dup2(fd[0], 0);
- }
-
- execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
- exit(EXIT_FAILURE);
- }
-
- if (mode[0] == 'r') {
- if (close(fd[1])) {
- err = errno;
- close(fd[0]);
- return io_strerror(T, err);
- }
- } else {
- if (close(fd[0])) {
- err = errno;
- close(fd[1]);
- return io_strerror(T, err);
- }
- fd[0] = fd[1];
- }
-
- /* make the pipe non-blocking */
- if (fcntl(fd[0], F_SETFL, O_NONBLOCK) == -1) {
- err = errno;
- close(fd[0]);
- return io_strerror(T, err);
- }
-
- stream_new(T, fd[0], lua_upvalueindex(1));
- return 1;
-}
diff --git a/test/popen.lua b/test/popen.lua
new file mode 100755
index 0000000..d13cbd0
--- /dev/null
+++ b/test/popen.lua
@@ -0,0 +1,40 @@
+#!bin/lem
+--
+-- This file is part of LEM, a Lua Event Machine.
+-- Copyright 2013 Emil Renner Berthing
+--
+-- LEM is free software: you can redistribute it and/or modify it
+-- under the terms of the GNU Lesser General Public License as
+-- published by the Free Software Foundation, either version 3 of
+-- the License, or (at your option) any later version.
+--
+-- LEM is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Lesser General Public License for more details.
+--
+-- You should have received a copy of the GNU Lesser General Public
+-- License along with LEM. If not, see <http://www.gnu.org/licenses/>.
+--
+
+package.path = '?.lua'
+package.cpath = '?.so'
+
+local utils = require 'lem.utils'
+local io = require 'lem.io'
+
+local write, format = io.write, string.format
+
+local p = assert(io.popen('telnet localhost 8080', 'rw'))
+
+assert(p:write('GET / HTTP/1.0'))
+assert(p:write('\n'))
+assert(p:write('\n'))
+
+local n = 0
+for line in p:lines() do
+ n = n+1
+ write(format('%4d: %s\n', n, line))
+end
+
+-- vim: syntax=lua ts=2 sw=2 noet: