From 5d2734de81f37bb38838fdc8420bf7a21e4f0bdf Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 18 Jun 2021 14:19:09 -0400 Subject: [PATCH] Merge reproc changes from dev. --- build.sh | 2 +- meson.build | 13 +- src/api/api.c | 2 + src/api/process.c | 430 ++++++++++++++++++++++++++++++++++++++++ src/meson.build | 4 +- subprojects/reproc.wrap | 4 + 6 files changed, 450 insertions(+), 5 deletions(-) create mode 100644 src/api/process.c create mode 100644 subprojects/reproc.wrap diff --git a/build.sh b/build.sh index 6f605797..eb1d420a 100755 --- a/build.sh +++ b/build.sh @@ -3,7 +3,7 @@ cflags+="-Wall -O3 -g -std=gnu11 -fno-strict-aliasing -Isrc -Ilib/font_renderer" cflags+=" $(pkg-config --cflags lua5.2) $(sdl2-config --cflags)" lflags="-static-libgcc -static-libstdc++" -for package in libagg freetype2 lua5.2 x11 libpcre2-8; do +for package in libagg freetype2 lua5.2 x11 libpcre2-8 reproc; do lflags+=" $(pkg-config --libs $package)" done lflags+=" $(sdl2-config --libs) -lm" diff --git a/meson.build b/meson.build index b1feef22..c1108ee7 100644 --- a/meson.build +++ b/meson.build @@ -10,13 +10,23 @@ libdl = cc.find_library('dl', required : false) libx11 = dependency('x11', required : false) lua_dep = dependency('lua5.2', required : false) pcre2_dep = dependency('libpcre2-8') +sdl_dep = dependency('sdl2', method: 'config-tool') if not lua_dep.found() lua_subproject = subproject('lua', default_options: ['shared=false', 'use_readline=false', 'app=false']) lua_dep = lua_subproject.get_variable('lua_dep') endif -sdl_dep = dependency('sdl2', method: 'config-tool') +reproc_subproject = subproject('reproc', default_options: ['default_library=static', 'multithreaded=false', 'reproc-cpp=false', 'examples=false']) +reproc_dep = reproc_subproject.get_variable('reproc_dep') + +lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl, libx11] + +if host_machine.system() == 'windows' + # Note that we need to explicitly add the windows socket DLL because + # the pkg-config file from reproc does not include it. + lite_deps += meson.get_compiler('cpp').find_library('ws2_32', required: true) +endif lite_cargs = [] if get_option('portable') @@ -55,4 +65,3 @@ endif subdir('lib/font_renderer') subdir('src') - diff --git a/src/api/api.c b/src/api/api.c index 5ea2e782..c479ca4a 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -4,12 +4,14 @@ int luaopen_system(lua_State *L); int luaopen_renderer(lua_State *L); int luaopen_regex(lua_State *L); +int luaopen_process(lua_State *L); static const luaL_Reg libs[] = { { "system", luaopen_system }, { "renderer", luaopen_renderer }, { "regex", luaopen_regex }, + { "process", luaopen_process }, { NULL, NULL } }; diff --git a/src/api/process.c b/src/api/process.c new file mode 100644 index 00000000..57c74d5c --- /dev/null +++ b/src/api/process.c @@ -0,0 +1,430 @@ +/** + * Basic binding of reproc into Lua. + * @copyright Jefferson Gonzalez + * @license MIT + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + reproc_t * process; + lua_State* L; + +} process_t; + +static int process_new(lua_State* L) +{ + process_t* self = (process_t*) lua_newuserdata( + L, sizeof(process_t) + ); + + memset(self, 0, sizeof(process_t)); + + self->process = NULL; + self->L = L; + + luaL_getmetatable(L, "PROCESS"); + lua_setmetatable(L, -2); + + return 1; +} + +static int process_strerror(lua_State* L) +{ + int error_code = luaL_checknumber(L, 1); + + if(error_code){ + lua_pushstring( + L, + reproc_strerror(error_code) + ); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int process_gc(lua_State* L) +{ + process_t* self = (process_t*) luaL_checkudata(L, 1, "PROCESS"); + + if(self->process){ + reproc_kill(self->process); + reproc_destroy(self->process); + self->process = NULL; + } + + return 0; +} + +static int process_start(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + luaL_checktype(L, 2, LUA_TTABLE); + + char* path = NULL; + size_t path_len = 0; + + if(lua_type(L, 3) == LUA_TSTRING){ + path = (char*) lua_tolstring(L, 3, &path_len); + } + + size_t deadline = 0; + + if(lua_type(L, 4) == LUA_TNUMBER){ + deadline = lua_tonumber(L, 4); + } + + size_t table_len = luaL_len(L, 2); + char* command[table_len+1]; + command[table_len] = NULL; + + int i; + for(i=1; i<=table_len; i++){ + lua_pushnumber(L, i); + lua_gettable(L, 2); + + command[i-1] = (char*) lua_tostring(L, -1); + + lua_remove(L, -1); + } + + if(self->process){ + reproc_kill(self->process); + reproc_destroy(self->process); + } + + self->process = reproc_new(); + + int out = reproc_start( + self->process, + (const char* const*) command, + (reproc_options){ + .working_directory = path, + .deadline = deadline, + .nonblocking=true, + .redirect.err.type=REPROC_REDIRECT_PIPE + } + ); + + if(out > 0) { + lua_pushboolean(L, 1); + } + else { + reproc_destroy(self->process); + self->process = NULL; + lua_pushnumber(L, out); + } + + return 1; +} + +static int process_pid(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + lua_pushnumber(L, reproc_pid(self->process)); + } else { + lua_pushnumber(L, 0); + } + + return 1; +} + +static int process_read(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + int read_size = 4096; + if (lua_type(L, 2) == LUA_TNUMBER){ + read_size = (int) lua_tonumber(L, 2); + } + + int tries = 1; + if (lua_type(L, 3) == LUA_TNUMBER){ + tries = (int) lua_tonumber(L, 3); + } + + int out = 0; + uint8_t buffer[read_size]; + + int runs; + for (runs=0; runsprocess, + REPROC_STREAM_OUT, + buffer, + read_size + ); + + if (out >= 0) + break; + } + + // if request for tries was set and nothing + // read kill the process + if(tries > 1 && out < 0) + out = REPROC_EPIPE; + + if(out == REPROC_EPIPE){ + reproc_kill(self->process); + reproc_destroy(self->process); + self->process = NULL; + + lua_pushnil(L); + } else if(out > 0) { + lua_pushlstring(L, (const char*) buffer, out); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + + return 1; +} + +static int process_read_errors(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + int read_size = 4096; + if (lua_type(L, 2) == LUA_TNUMBER){ + read_size = (int) lua_tonumber(L, 2); + } + + int tries = 1; + if (lua_type(L, 3) == LUA_TNUMBER){ + tries = (int) lua_tonumber(L, 3); + } + + int out = 0; + uint8_t buffer[read_size]; + + int runs; + for (runs=0; runsprocess, + REPROC_STREAM_ERR, + buffer, + read_size + ); + + if (out >= 0) + break; + } + + // if request for tries was set and nothing + // read kill the process + if(tries > 1 && out < 0) + out = REPROC_EPIPE; + + if(out == REPROC_EPIPE){ + reproc_kill(self->process); + reproc_destroy(self->process); + self->process = NULL; + + lua_pushnil(L); + } else if(out > 0) { + lua_pushlstring(L, (const char*) buffer, out); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + + return 1; +} + +static int process_write(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + size_t data_size = 0; + const char* data = luaL_checklstring(L, 2, &data_size); + + int out = 0; + + out = reproc_write( + self->process, + (uint8_t*) data, + data_size + ); + + if(out == REPROC_EPIPE){ + reproc_kill(self->process); + reproc_destroy(self->process); + self->process = NULL; + } + + lua_pushnumber(L, out); + } else { + lua_pushnumber(L, REPROC_EPIPE); + } + + return 1; +} + +static int process_close_stream(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + size_t stream = luaL_checknumber(L, 2); + + int out = reproc_close(self->process, stream); + + lua_pushnumber(L, out); + } else { + lua_pushnumber(L, REPROC_EINVAL); + } + + return 1; +} + +static int process_wait(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + size_t timeout = luaL_checknumber(L, 2); + + int out = reproc_wait(self->process, timeout); + + if(out >= 0){ + reproc_destroy(self->process); + self->process = NULL; + } + + lua_pushnumber(L, out); + } else { + lua_pushnumber(L, REPROC_EINVAL); + } + + return 1; +} + +static int process_terminate(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + int out = reproc_terminate(self->process); + + if(out < 0){ + lua_pushnumber(L, out); + } else { + reproc_destroy(self->process); + self->process = NULL; + lua_pushboolean(L, 1); + } + } else { + lua_pushnumber(L, REPROC_EINVAL); + } + + return 1; +} + +static int process_kill(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + int out = reproc_kill(self->process); + + if(out < 0){ + lua_pushnumber(L, out); + } else { + reproc_destroy(self->process); + self->process = NULL; + lua_pushboolean(L, 1); + } + } else { + lua_pushnumber(L, REPROC_EINVAL); + } + + return 1; +} + +static int process_running(lua_State* L) +{ + process_t* self = (process_t*) lua_touserdata(L, 1); + + if(self->process){ + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + + return 1; +} + +static const struct luaL_Reg process_methods[] = { + { "__gc", process_gc}, + {"start", process_start}, + {"pid", process_pid}, + {"read", process_read}, + {"read_errors", process_read_errors}, + {"write", process_write}, + {"close_stream", process_close_stream}, + {"wait", process_wait}, + {"terminate", process_terminate}, + {"kill", process_kill}, + {"running", process_running}, + {NULL, NULL} +}; + +static const struct luaL_Reg process[] = { + {"new", process_new}, + {"strerror", process_strerror}, + {"ERROR_PIPE", NULL}, + {"ERROR_WOULDBLOCK", NULL}, + {"ERROR_TIMEDOUT", NULL}, + {"STREAM_STDIN", NULL}, + {"STREAM_STDOUT", NULL}, + {"STREAM_STDERR", NULL}, + {NULL, NULL} +}; + +int luaopen_process(lua_State *L) +{ + luaL_newmetatable(L, "PROCESS"); + luaL_setfuncs(L, process_methods, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + luaL_newlib(L, process); + + lua_pushnumber(L, REPROC_EPIPE); + lua_setfield(L, -2, "ERROR_PIPE"); + + lua_pushnumber(L, REPROC_EWOULDBLOCK); + lua_setfield(L, -2, "ERROR_WOULDBLOCK"); + + lua_pushnumber(L, REPROC_ETIMEDOUT); + lua_setfield(L, -2, "ERROR_TIMEDOUT"); + + lua_pushnumber(L, REPROC_STREAM_IN); + lua_setfield(L, -2, "STREAM_STDIN"); + + lua_pushnumber(L, REPROC_STREAM_OUT); + lua_setfield(L, -2, "STREAM_STDOUT"); + + lua_pushnumber(L, REPROC_STREAM_ERR); + lua_setfield(L, -2, "STREAM_STDERR"); + + return 1; +} diff --git a/src/meson.build b/src/meson.build index faa1a8ea..9516c94d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ lite_sources = [ 'api/renderer_font.c', 'api/regex.c', 'api/system.c', + 'api/process.c', 'renderer.c', 'renwindow.c', 'fontdesc.c', @@ -19,11 +20,10 @@ endif executable('lite', lite_sources + lite_rc, include_directories: [lite_include, font_renderer_include], - dependencies: [lua_dep, sdl_dep, pcre2_dep, libm, libdl, libx11], + dependencies: lite_deps, c_args: lite_cargs, link_with: libfontrenderer, link_args: lite_link_args, install: true, gui_app: true, ) - diff --git a/subprojects/reproc.wrap b/subprojects/reproc.wrap new file mode 100644 index 00000000..f1afb4fa --- /dev/null +++ b/subprojects/reproc.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = reproc +url = https://github.com/franko/reproc +revision = v14.2.2-meson-1