/* * gcsd * * gcsd the generic command sequencer daemon can be used to serialize * commands sent over various paralell communication channels to a * single command output. It can be seen as a multiplexer for any * kind of communication between a single resource and various clients * which want to submit commands to it or query information from it. * gcsd is written in C and Lua. The goal is to provide an easy to * understand high level API based on Lua which can be used to * implement the business logic of the so formed multiplexer daemon. * * * Copyright (C) 2009-2010 Markus Grueneis * Christian Pointner * * This file is part of gcsd. * * gcsd is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * gcsd 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with gcsd. If not, see . */ #include #include #include #include #include #include #include "datatypes.h" #include "config.h" #include "options.h" #include "string_list.h" #include "log.h" #include "l_rawio.h" #include "l_tcp.h" #include "l_util.h" #include "l_timer.h" #include "l_log.h" #include "l_sig_handler.h" #ifndef WINVER #include "daemon.h" #endif extern const char gcsd_lua_bytecode[]; extern const size_t gcsd_lua_bytecode_len; #define LUA_MAIN_LOOP_FUNC "main_loop" #if LUA_VERSION_NUM > 501 static const luaL_Reg gcsd_lualibs[] = { {"_G", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_RAWIOLIBNAME, luaopen_rawio}, {LUA_TCPLIBNAME, luaopen_tcp}, {LUA_UTILLIBNAME, luaopen_util}, {LUA_TIMERLIBNAME, luaopen_timer}, {LUA_LOGLIBNAME, luaopen_log}, {LUA_SIGNALLIBNAME, luaopen_signal}, {NULL, NULL} }; #else static const luaL_Reg gcsd_lualibs[] = { {"", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_RAWIOLIBNAME, luaopen_rawio}, {LUA_TCPLIBNAME, luaopen_tcp}, {LUA_UTILLIBNAME, luaopen_util}, {LUA_TIMERLIBNAME, luaopen_timer}, {LUA_LOGLIBNAME, luaopen_log}, {LUA_SIGNALLIBNAME, luaopen_signal}, {NULL, NULL} }; #endif int init_defines(lua_State *L) { lua_newtable(L); lua_pushliteral(L, "OK"); lua_pushinteger(L, 0); lua_settable(L, -3); lua_pushliteral(L, "KILL_CLIENT"); lua_pushinteger(L, 1); lua_settable(L, -3); lua_pushliteral(L, "KILL_MODULE"); lua_pushinteger(L, 2); lua_settable(L, -3); lua_pushliteral(L, "KILL_MODULE_CLASS"); lua_pushinteger(L, 3); lua_settable(L, -3); lua_pushliteral(L, "KILL_DAEMON"); lua_pushinteger(L, 4); lua_settable(L, -3); lua_pushliteral(L, "IN_MODULE"); lua_pushinteger(L, 1); lua_settable(L, -3); lua_pushliteral(L, "OUT_MODULE"); lua_pushinteger(L, 2); lua_settable(L, -3); lua_pushliteral(L, "INOUT_MODULE"); lua_pushinteger(L, 3); lua_settable(L, -3); lua_pushliteral(L, "MISC_MODULE"); lua_pushinteger(L, 4); lua_settable(L, -3); lua_pushliteral(L, "MODULES_PATH"); lua_pushstring(L, MODULESDIR); lua_settable(L, -3); lua_setglobal(L, "defines"); return 0; } int init_main_loop(lua_State *L) { const luaL_Reg *lib = gcsd_lualibs; #if LUA_VERSION_NUM > 501 for (; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); } #else for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } #endif int ret = luaL_loadbuffer(L, gcsd_lua_bytecode, gcsd_lua_bytecode_len, "gcsd"); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRSYNTAX: log_printf(ERROR, "luaL_loadbuffer() syntax error: %s", err_str); break; case LUA_ERRMEM: log_printf(ERROR, "luaL_loadbuffer() malloc error: %s", err_str); break; default: log_printf(ERROR, "luaL_loadbuffer() unknown error: %s", err_str); break; } return -1; } ret = lua_pcall(L, 0, 0, 0); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRRUN: log_printf(ERROR, "lua_pcall() runtime error: %s", err_str); break; case LUA_ERRMEM: log_printf(ERROR, "lua_pcall() malloc error: %s", err_str); break; case LUA_ERRERR: log_printf(ERROR, "lua_pcall() error at error handler function: %s", err_str); break; } return -1; } return init_defines(L); } int call_main_loop(lua_State* L, options_t* opt) { lua_getglobal(L, LUA_MAIN_LOOP_FUNC); if(!lua_isfunction(L, -1)) { log_printf(ERROR, "there is no function '%s' inside gcsd bytecode", LUA_MAIN_LOOP_FUNC); return -1; }; options_lua_push(opt, L); int ret = lua_pcall(L, 1, LUA_MULTRET, 0); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(%s) runtime error: %s", LUA_MAIN_LOOP_FUNC, err_str); break; case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(%s) malloc error: %s", LUA_MAIN_LOOP_FUNC, err_str); break; case LUA_ERRERR: log_printf(ERROR, "lua_pcall(%s) error at error handler function: %s", LUA_MAIN_LOOP_FUNC, err_str); break; } return -1; } int n = lua_gettop(L); log_printf(DEBUG, "%s returned %d values", LUA_MAIN_LOOP_FUNC, n); int i; for (i = 1; i <= n; i++) log_printf(DEBUG, "return value [%d] = '%s'", i, luaL_checkstring(L, i)); ret = lua_tointeger(L, 1); return ret; } int main_loop(options_t* opt) { lua_State *L; L = luaL_newstate(); if(!L) { log_printf(ERROR, "error creating lua state"); return -1; } int ret = init_main_loop(L); if(!ret) ret = call_main_loop(L, opt); lua_close(L); return ret; } int main(int argc, char* argv[]) { log_init(); options_t opt; int ret = options_parse(&opt, argc, argv); if(ret) { if(ret > 0) { fprintf(stderr, "syntax error near: %s\n\n", argv[ret]); } if(ret == -2) { fprintf(stderr, "memory error on options_parse, exitting\n"); } if(ret == -3) { options_print_version(); } if(ret != -2 && ret != -3) options_print_usage(); if(ret == -1 || ret == -3) ret = 0; options_clear(&opt); log_close(); exit(ret); } string_list_element_t* tmp = opt.log_targets_.first_; while(tmp) { ret = log_add_target(tmp->string_); if(ret) { switch(ret) { case -2: fprintf(stderr, "memory error on log_add_target, exitting\n"); break; case -3: fprintf(stderr, "unknown log target: '%s', exitting\n", tmp->string_); break; case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", tmp->string_); break; default: fprintf(stderr, "syntax error near: '%s', exitting\n", tmp->string_); break; } options_clear(&opt); log_close(); exit(ret); } tmp = tmp->next_; } log_printf(NOTICE, "just started..."); options_parse_post(&opt); #ifndef WINVER priv_info_t priv; if(opt.username_) if(priv_init(&priv, opt.username_, opt.groupname_)) { options_clear(&opt); log_close(); exit(-1); } #endif #ifndef WINVER FILE* pid_file = NULL; if(opt.pid_file_) { pid_file = fopen(opt.pid_file_, "w"); if(!pid_file) { log_printf(WARNING, "unable to open pid file: %s", strerror(errno)); } } if(opt.chroot_dir_) if(do_chroot(opt.chroot_dir_)) { options_clear(&opt); log_close(); exit(-1); } if(opt.username_) if(priv_drop(&priv)) { options_clear(&opt); log_close(); exit(-1); } if(opt.daemonize_) { pid_t oldpid = getpid(); daemonize(); log_printf(INFO, "running in background now (old pid: %d)", oldpid); } if(pid_file) { pid_t pid = getpid(); fprintf(pid_file, "%d", pid); fclose(pid_file); } #endif ret = main_loop(&opt); options_clear(&opt); if(!ret) log_printf(NOTICE, "normal shutdown"); else if(ret < 0) log_printf(NOTICE, "shutdown after error"); else log_printf(NOTICE, "shutdown after signal"); log_close(); return ret; }