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
|
# Copyright (c) 2010-2011 Bart Van Der Meerssche
|
||||||
# $Id$
|
|
||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ include $(INCLUDE_DIR)/package.mk
|
||||||
define Package/flukso
|
define Package/flukso
|
||||||
SECTION:=utils
|
SECTION:=utils
|
||||||
CATEGORY:=Utilities
|
CATEGORY:=Utilities
|
||||||
DEPENDS:=+ntpclient +nixio +rrdtool1
|
DEPENDS:=+libdaemon +ntpclient +liblua +luci-nixio +rrdtool1
|
||||||
TITLE:=Flukso - community metering
|
TITLE:=Flukso - community metering
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
@ -24,18 +23,19 @@ endef
|
||||||
|
|
||||||
define Build/Prepare
|
define Build/Prepare
|
||||||
mkdir -p $(PKG_BUILD_DIR)
|
mkdir -p $(PKG_BUILD_DIR)
|
||||||
|
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||||
$(CP) ./luasrc/* $(PKG_BUILD_DIR)/
|
$(CP) ./luasrc/* $(PKG_BUILD_DIR)/
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|
||||||
define Build/Compile
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Package/flukso/install
|
define Package/flukso/install
|
||||||
$(INSTALL_DIR) $(1)/usr/lib/lua
|
$(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
|
$(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
|
endef
|
||||||
|
|
||||||
$(eval $(call BuildPackage,flukso))
|
$(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