From 501d84381c8603e5680b86d65afeb633279116f9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 29 Jan 2013 18:31:31 +0100 Subject: io: rework io.popen to use posix_spawn() ..and accept 'rw' flag to make reading _and_ writing to the process' stdin and stdout possible. --- lem/io/core.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lem/io/stream.c | 60 ------------------------------------------------ test/popen.lua | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 61 deletions(-) create mode 100755 test/popen.lua 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 #include #include +#include #if defined(__FreeBSD__) || defined(__APPLE__) #include #include #include +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 . +-- + +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: -- cgit v1.2.1