openwrt: luad is a libdaemon-based generic daemonization framework for Lua code

This commit is contained in:
Bart Van Der Meerssche 2011-01-07 14:41:46 +01:00
parent 3e258f13f8
commit 221636f5cf
4 changed files with 411 additions and 9 deletions

View File

@ -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))

View File

@ -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')

View File

@ -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;
}
}

View File

@ -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