/* luad.c - A libdaemon-based generic daemonization framework for Lua code. Copyright (C) 2003-2008 Lennart Poettering 2011 Bart Van Der Meerssche 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 . */ /* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef OPENWRT_BUILD #include #include #include #else #include #include #include #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_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; } }