openwrt: luad is a libdaemon-based generic daemonization framework for Lua code
This commit is contained in:
parent
3e258f13f8
commit
221636f5cf
|
@ -1,5 +1,4 @@
|
|||
# Copyright (c) 2010 flukso.net
|
||||
# $Id$
|
||||
# Copyright (c) 2010-2011 Bart Van Der Meerssche
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
|
@ -14,7 +13,7 @@ include $(INCLUDE_DIR)/package.mk
|
|||
define Package/flukso
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
DEPENDS:=+ntpclient +nixio +rrdtool1
|
||||
DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1
|
||||
TITLE:=Flukso - community metering
|
||||
endef
|
||||
|
||||
|
@ -24,18 +23,19 @@ endef
|
|||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
$(CP) ./luasrc/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/flukso/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua
|
||||
$(CP) $(PKG_BUILD_DIR)/dbg.lua $(1)/usr/lib/lua/
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/dbg.lua $(1)/usr/lib/lua/
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/rrd
|
||||
$(CP) $(PKG_BUILD_DIR)/rrd/*.lua $(1)/usr/lib/lua/rrd/
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/rrd/*.lua $(1)/usr/lib/lua/rrd/
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/luad $(1)/usr/sbin/
|
||||
$(LN) /usr/sbin/luad $(1)/usr/sbin/spid
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/spi/spid.lua $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,flukso))
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#! /usr/bin/env lua
|
||||
|
||||
--[[
|
||||
|
||||
spid.lua - Lua part of the spi daemon
|
||||
|
||||
Copyright (C) 2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
|
||||
|
||||
This program 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
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
require 'nixio.fs'
|
||||
|
||||
ctrl_path = os.getenv('DAEMON_PATH') .. '/ctrl/'
|
||||
|
||||
nixio.fs.mkdirr(ctrl_path)
|
||||
nixio.fs.unlink(ctrl_path .. 'in')
|
||||
nixio.fs.unlink(ctrl_path .. 'out')
|
||||
|
||||
nixio.fs.mkfifo(ctrl_path .. 'in', '644')
|
||||
nixio.fs.mkfifo(ctrl_path .. 'out', '644')
|
||||
|
||||
rdwr_nonblock = nixio.open_flags('rdwr', 'nonblock')
|
||||
|
||||
ctrl_fd_in = nixio.open(ctrl_path .. 'in', rdwr_nonblock)
|
||||
ctrl_fd_out = nixio.open(ctrl_path .. 'out', rdwr_nonblock)
|
||||
|
||||
pfin = nixio.poll_flags('in')
|
||||
|
||||
local fd = {fd = ctrl_fd_in,
|
||||
events = pfin,
|
||||
revents = 0}
|
||||
|
||||
local fds = {fd}
|
||||
|
||||
ctrl_line = ctrl_fd_in:linesource()
|
||||
|
||||
while true do
|
||||
if nixio.poll(fds, 1000) then
|
||||
ctrl_fd_out:write((ctrl_line() or 'nil!') .. '\n')
|
||||
else
|
||||
ctrl_fd_out:write('timeout\n')
|
||||
end
|
||||
end
|
||||
|
||||
nixio.fs.unlink(ctrl_path .. 'in')
|
||||
nixio.fs.unlink(ctrl_path .. 'out')
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
|
||||
luad.c - A libdaemon-based generic daemonization framework for Lua code.
|
||||
|
||||
Copyright (C) 2003-2008 Lennart Poettering
|
||||
2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
|
||||
|
||||
This program 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
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* Enable GNU extensions so we can use asprintf */
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#ifdef OPENWRT_BUILD
|
||||
#define DAEMON_USER "flukso"
|
||||
#define DAEMON_GROUP "flukso"
|
||||
#else
|
||||
#define DAEMON_USER "icarus75"
|
||||
#define DAEMON_GROUP "icarus75"
|
||||
#endif
|
||||
|
||||
#define DAEMON_VARRUN "/var/run"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libdaemon/dfork.h>
|
||||
#include <libdaemon/dlog.h>
|
||||
#include <libdaemon/dpid.h>
|
||||
#include <libdaemon/dexec.h>
|
||||
|
||||
#ifdef OPENWRT_BUILD
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
#else
|
||||
#include <lua5.1/lua.h>
|
||||
#include <lua5.1/lualib.h>
|
||||
#include <lua5.1/lauxlib.h>
|
||||
#endif
|
||||
|
||||
static void sigterm(int signo)
|
||||
{
|
||||
daemon_log(LOG_INFO, "Caught a SIGTERM. Exiting... ");
|
||||
daemon_pid_file_remove();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static const char *daemon_pid_file_proc_override(void)
|
||||
{
|
||||
char *fn;
|
||||
|
||||
asprintf(&fn, "%s/%s/pid", DAEMON_VARRUN, daemon_log_ident);
|
||||
return fn;
|
||||
}
|
||||
|
||||
static int drop_root(void)
|
||||
{
|
||||
struct passwd *pw;
|
||||
struct group * gr;
|
||||
|
||||
if (!(pw = getpwnam(DAEMON_USER))) {
|
||||
daemon_log(LOG_ERR, "Failed to find user '"DAEMON_USER"'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(gr = getgrnam(DAEMON_GROUP))) {
|
||||
daemon_log(LOG_ERR, "Failed to find group '"DAEMON_GROUP"'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (initgroups(DAEMON_USER, gr->gr_gid) != 0) {
|
||||
daemon_log(LOG_ERR, "Failed to change group list: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setregid(gr->gr_gid, gr->gr_gid) < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to change GID: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setreuid(pw->pw_uid, pw->pw_uid) < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to change UID: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
setenv("USER", pw->pw_name, 1);
|
||||
setenv("LOGNAME", pw->pw_name, 1);
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_runtime_dir(char **pruntime_path)
|
||||
{
|
||||
int r = -1;
|
||||
mode_t u;
|
||||
int reset_umask = 0;
|
||||
|
||||
struct passwd *pw;
|
||||
struct group * gr;
|
||||
struct stat st;
|
||||
|
||||
asprintf(pruntime_path, "%s/%s", DAEMON_VARRUN, daemon_log_ident);
|
||||
|
||||
if (!(pw = getpwnam(DAEMON_USER))) {
|
||||
daemon_log(LOG_ERR, "Failed to find user '"DAEMON_USER"'");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(gr = getgrnam(DAEMON_GROUP))) {
|
||||
daemon_log(LOG_ERR, "Failed to find group '"DAEMON_GROUP"'");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u = umask(0000);
|
||||
reset_umask = 1;
|
||||
|
||||
if (mkdir(*pruntime_path, 0755) < 0 && errno != EEXIST) {
|
||||
daemon_log(LOG_ERR, "mkdir(\"%s\"): %s", *pruntime_path, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chown(*pruntime_path, pw->pw_uid, gr->gr_gid);
|
||||
|
||||
if (stat(*pruntime_path, &st) < 0) {
|
||||
daemon_log(LOG_ERR, "stat(\"%s\"): %s\n", *pruntime_path, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
|
||||
daemon_log(LOG_ERR, "Failed to create runtime directory \"%s\"", *pruntime_path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
if (reset_umask)
|
||||
umask(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pid_t pid;
|
||||
struct sigaction sa;
|
||||
|
||||
lua_State *L = NULL;
|
||||
char *luad_path = NULL;
|
||||
char *runtime_path = NULL;
|
||||
|
||||
/* Reset signal handlers */
|
||||
if (daemon_reset_sigs(-1) < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to reset all signal handlers: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Unblock signals */
|
||||
if (daemon_unblock_sigs(-1) < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to unblock all signals: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set the daemon's syslog identification string */
|
||||
daemon_log_ident = daemon_ident_from_argv0(argv[0]);
|
||||
|
||||
/* Set the pid file to /var/run/<daemon>/<pid> */
|
||||
daemon_pid_file_proc = daemon_pid_file_proc_override;
|
||||
|
||||
/* Check if we are called with -k parameter */
|
||||
if (argc >= 2 && !strcmp(argv[1], "-k")) {
|
||||
int ret;
|
||||
|
||||
/* Kill daemon with SIGTERM */
|
||||
|
||||
/* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */
|
||||
if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0) {
|
||||
daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return ret < 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
if (getuid() != 0) {
|
||||
daemon_log(LOG_ERR, "This daemon should be run as root.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check that the daemon is not run twice a the same time */
|
||||
if ((pid = daemon_pid_file_is_running()) >= 0) {
|
||||
daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prepare for return value passing from the initialization procedure of the daemon process */
|
||||
if (daemon_retval_init() < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to create pipe.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Do the fork */
|
||||
if ((pid = daemon_fork()) < 0) {
|
||||
|
||||
/* Exit on error */
|
||||
daemon_retval_done();
|
||||
return 1;
|
||||
}
|
||||
else if (pid) { /* The parent */
|
||||
int ret;
|
||||
|
||||
/* Wait for 20 seconds for the return value passed from the daemon process */
|
||||
if ((ret = daemon_retval_wait(20)) < 0) {
|
||||
daemon_log(LOG_ERR, "Could not recieve return value from daemon process: %s", strerror(errno));
|
||||
return 255;
|
||||
}
|
||||
|
||||
daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret);
|
||||
return ret;
|
||||
}
|
||||
else { /* The daemon */
|
||||
/* Close FDs */
|
||||
if (daemon_close_all(-1) < 0) {
|
||||
daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno));
|
||||
|
||||
/* Send the error condition to the parent process */
|
||||
daemon_retval_send(1);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Create the daemon runtime dir */
|
||||
if (make_runtime_dir(&runtime_path) < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Drop root priviledges */
|
||||
if (drop_root() < 0) {
|
||||
daemon_log(LOG_ERR, "Could not drop root privileges for %s/%s", DAEMON_USER, DAEMON_GROUP);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Create the PID file */
|
||||
if (daemon_pid_file_create() < 0) {
|
||||
daemon_log(LOG_ERR, "Could not create PID file (%s)", strerror(errno));
|
||||
daemon_retval_send(2);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Initialize signal handling */
|
||||
sa.sa_handler = sigterm;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGTERM);
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if(sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||
daemon_log(LOG_ERR, "Cannot catch SIGTERM: %s", strerror(errno));
|
||||
daemon_retval_send(3);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Set environment vars for the Lua daemon */
|
||||
setenv("DAEMON", daemon_log_ident, 1);
|
||||
setenv("DAEMON_PATH", runtime_path, 1);
|
||||
|
||||
/* Send OK to parent process */
|
||||
daemon_retval_send(0);
|
||||
daemon_log(LOG_INFO, "Sucessfully started with DEAMON=%s and DAEMON_PATH=%s", daemon_log_ident, runtime_path);
|
||||
|
||||
/* Create a new Lua environment */
|
||||
L = luaL_newstate();
|
||||
/* And load the standard libraries into the Lua environment */
|
||||
luaL_openlibs(L);
|
||||
/* Derive the Lua daemon path from the C daemon one */
|
||||
asprintf(&luad_path, "%s%s", (const char *)argv[0], ".lua");
|
||||
/* Tunnel through the wormhole into Lua neverland. This call should never return. */
|
||||
if (luaL_dofile(L, (const char *)luad_path)) {
|
||||
daemon_log(LOG_ERR, "Lua returned with error message: %s", lua_tostring(L,-1));
|
||||
}
|
||||
/* Clean up the Lua state */
|
||||
lua_close(L);
|
||||
|
||||
|
||||
/* Do a cleanup */
|
||||
finish:
|
||||
daemon_log(LOG_INFO, "Exiting...");
|
||||
daemon_retval_send(255);
|
||||
daemon_pid_file_remove();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright (c) 2011 Bart Van Der Meerssche
|
||||
|
||||
# Compiler flag to set the C Standard level.
|
||||
# c89 = "ANSI" C
|
||||
# gnu89 = c89 plus GCC extensions
|
||||
# c99 = ISO C99 standard (not yet fully implemented)
|
||||
# gnu99 = c99 plus GCC extensions
|
||||
CSTANDARD = -std=gnu89
|
||||
|
||||
all: luad.o
|
||||
ifeq ($(OPENWRT_BUILD),1)
|
||||
$(CC) $(LDFLAGS) $(CSTANDARD) -ldl -lm -lcrypt -ldaemon -llua luad.o -o luad
|
||||
else
|
||||
$(CC) $(LDFLAGS) $(CSTANDARD) -ldl -lm -lcrypt -ldaemon -llua5.1 luad.o -o luad
|
||||
endif
|
||||
|
||||
luad.o: luad.c
|
||||
ifeq ($(OPENWRT_BUILD),1)
|
||||
$(CC) $(CFLAGS) $(CSTANDARD) -Wall -D OPENWRT_BUILD=1 -c luad.c
|
||||
else
|
||||
$(CC) $(CFLAGS) $(CSTANDARD) -Wall -c luad.c
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm *.o luad
|
Loading…
Reference in New Issue