author | Michael Krelin <hacker@klever.net> | 2004-07-23 21:31:57 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2004-07-23 21:31:57 (UTC) |
commit | 125671c860a82643d36bc3da279d0b831fae4b34 (patch) (side-by-side diff) | |
tree | ab5edcac7c58e769c9d8436ae0e803edb2ebca9f | |
parent | 0a7f5999eb47ce113b3cd47b03198947441945a7 (diff) | |
download | dudki-125671c860a82643d36bc3da279d0b831fae4b34.zip dudki-125671c860a82643d36bc3da279d0b831fae4b34.tar.gz dudki-125671c860a82643d36bc3da279d0b831fae4b34.tar.bz2 |
ability to send arbitrary signals
-rw-r--r-- | NEWS.xml | 2 | ||||
-rw-r--r-- | man/dudki.8.in | 11 | ||||
-rw-r--r-- | src/dudki.cc | 20 |
3 files changed, 29 insertions, 4 deletions
@@ -1,14 +1,14 @@ <?xml version="1.0" encoding="us-ascii"?> <news> <version version="0.2" date="July 23rd, 2004"> - <ni>now dudki sends signals to the processes being monitored from the command line</ni> + <ni>now dudki sends arbitrary signals to the processes being monitored from the command line</ni> </version> <version version="0.1" date="July 21st, 2004"> <ni><kbd>initgroups()</kbd> called before executing <kbd>RestartCommand</kbd></ni> <ni>more civilized way of restarting on <kbd>SIGHUP</kbd></ni> <ni>minor changes to build process</ni> </version> <version version="0.0" date="July 11th, 2004"> <ni>Initial release</ni> </version> </news> diff --git a/man/dudki.8.in b/man/dudki.8.in index 05db733..be60f53 100644 --- a/man/dudki.8.in +++ b/man/dudki.8.in @@ -1,128 +1,135 @@ -.TH dudki 8 "June 9th, 2004" "dudki(8)" "Klever Group (http://www.klever.net/)" +.TH dudki 8 "July 23rd, 2004" "dudki(8)" "Klever Group (http://www.klever.net/)" .hla en .ds longopt @HAVE_GETOPT_LONG@ .SH NAME -dudki \- a process monitoring daemon +dudki \- a process maintenance daemon .SH SYNOPSYS \fBdudki\fR [\fB-h\fR] .if \*[longopt] [\fB--help\fR] [\fB--usage\fR] [\fB-V\fR] .if \*[longopt] [\fB--version\fR] [\fB-L\fR] .if \*[longopt] [\fB--license\fR] [\fB-f\fR \fIconfigfile\fR] .if \*[longopt] [\fB--config=\fR\fIconfigfile\fR] [\fB-k\fR] .if \*[longopt] [\fB--kill\fR] [\fB-r\fR] .if \*[longopt] [\fB--reload\fR] [\fB-c\fR] .if \*[longopt] [\fB--check\fR] +[\fB-s\fR \fIsignum\fR] +.if \*[longopt] [\fB--signal=\fR\fIsignum\fR] [\fB-e\fR] .if \*[longopt] [\fB--ensure\fR] [\fB-t\fR] .if \*[longopt] [\fB--test\fR] [\fI<process-list>\fR] .SH DESCRIPTION dudki daemon is designed to run in the background and periodically check if certain processes specified in the configuration file are running. If a process is detected as dead dudki tries to restart it using the command line specified in the configuration file and notifies the specified contact (currently only via email). .SH OPTIONS .TP .ie \*[longopt] \fB-f\fR \fIconfigfile\fR, \fB--config=\fR\fIconfigfile\fR .el \fB-f\fR \fIconfigfile\fR Specify the configuration file to use (default is \fI@sysconfdir@/dudki.conf\fR). .TP .ie \*[longopt] \fB-k\fR, \fB--kill\fR .el \fB-k\fR Stop the running instance by sending the \fBSIGTERM\fR signal. If no process name specified on the command line, dudki kills his own running instance. .TP .ie \*[longopt] \fB-r\fR, \fB--reload\fR .el \fB-r\fR Reload the running instance by sending the \fBSIGHUP\fR signal. Like with \fB-k\fR, if no process name specified on the command line, dudki sends \fBSIGHUP\fR to his own running instance. .TP +.ie \*[longopt] \fB-s\fR \fIsignum\fR, \fB--signal=\fR\fIsignum\fR +.el \fB-s\fR \fIsignum\fR +Send arbitrary signal to the names processes (or self, but it doesn't make much +sense). +.TP .ie \*[longopt] \fB-c\fR, \fB--check\fR .el \fB-c\fR Check if dudki is running. Exit with non-zero status if not. The same target rules as in \fB-k\fR and \fB-r\fR apply here. .TP .ie \*[longopt] \fB-e\fR, \fB--ensure\fR .el \fB-e\fR Ensure that dudki is running. Load, if not. Useful for running as a cron job once in a while. If the daemon is running runs quietly providing no output. .TP .ie \*[longopt] \fB-t\fR, \fB--test\fR .el \fB-t\fR Check the syntax of configuration file and exit. .TP .ie \*[longopt] \fB-h\fR, \fB--help\fR, \fB--usage\fR .el \fB-h\fR Display short usage message and exit. .TP .ie \*[longopt] \fB-V\fR, \fB--version\fR .el \fB-V\fR Report version and exit. .TP .ie \*[longopt] \fB-L\fR, \fB--license\fR .el \fB-L\fR Show licensing terms. .SH EXIT STATUS Zero in case of success, non-zero otherwise. .SH FILES .TP @sysconfdir@/dudki.conf Default configuration file. .TP /var/run/dudki.pid Default pid-file for the dudki process. .SH AUTHOR Written by Michael 'hacker' Krelin <hacker@klever.net> .SH COPYRIGHT Copyright (c) 2004 Klever Group (http://www.klever.net/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .SH BUGS You tell me. Send reports to <dudki-bugs@klever.net> .SH SEE ALSO \fBdudki.conf\fR(5) diff --git a/src/dudki.cc b/src/dudki.cc index e91ad5e..9562079 100644 --- a/src/dudki.cc +++ b/src/dudki.cc @@ -1,269 +1,287 @@ #include <unistd.h> #include <signal.h> #include <syslog.h> +#include <errno.h> #include <iostream> #include <fstream> #include <stdexcept> using namespace std; #include "configuration.h" #include "util.h" #include "config.h" #ifdef HAVE_GETOPT_H # include <getopt.h> #endif #ifndef DEFAULT_CONF_FILE # define DEFAULT_CONF_FILE "/etc/dudki.conf" #endif #define PHEADER PACKAGE " Version " VERSION #define PCOPY "Copyright (c) 2004 Klever Group" bool finishing = false; bool restarting = false; static char **_argv = NULL; static void lethal_signal_handler(int signum) { syslog(LOG_NOTICE,"Lethal signal received. Terminating."); finishing = true; } static void sighup_handler(int signum) { syslog(LOG_NOTICE,"SUGHUP received, reloading."); restarting = finishing = true; } void check_herd(configuration& config) { for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) i->second.check(i->first,config); } void signal_self(const configuration& config,int signum) { ifstream pids(config.pidfile.c_str(),ios::in); if(!pids) throw runtime_error("Can't detect running instance"); pid_t pid = 0; pids >> pid; if(!pid) throw runtime_error("Can't detect running instance"); if(pid==getpid()) throw 0; if(kill(pid,signum)) throw runtime_error("Failed to signal running instance"); } int main(int argc,char **argv) { try { _argv = new char*[argc+1]; if(!_argv) throw runtime_error("memory allocation problem at the very start"); memmove(_argv,argv,sizeof(*_argv)*(argc+1)); string config_file = DEFAULT_CONF_FILE; enum { op_default, op_work, op_signal, op_ensure, op_test } op = op_default; int op_signum = 0; while(true) { -#define SHORTOPTSTRING "f:hVLrkcet" +#define SHORTOPTSTRING "f:hVLrkcets:" #ifdef HAVE_GETOPT_LONG static struct option opts[] = { { "help", no_argument, 0, 'h' }, { "usage", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "license", no_argument, 0, 'L' }, { "config", required_argument, 0, 'f' }, { "kill", no_argument, 0, 'k' }, { "reload", no_argument, 0, 'r' }, + { "signal", required_argument, 0, 's' }, { "check", no_argument, 0, 'c' }, { "ensure", no_argument, 0, 'e' }, { "test", no_argument, 0, 't' }, { NULL, 0, 0, 0 } }; int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); #else /* !HAVE_GETOPT_LONG */ int c = getopt(argc,argv,SHORTOPTSTRING); #endif /* /HAVE_GETOPT_LONG */ if(c==-1) break; switch(c) { case 'h': cerr << PHEADER << endl << PCOPY << endl << endl << " " << argv[0] << " [options] [processes]" << endl << endl << #ifdef HAVE_GETOPT_LONG " -h, --help\n" " --usage display this text\n" " -V, --version display version number\n" " -L, --license show license\n" " -f filename, --config=filename\n" " specify the configuration file to use\n" "\n" " -k, --kill stop running instance (send SIGTERM)\n" " -r, --reload reload running instance (send SIGHUP)\n" + " -s signum, --signal=signum\n" + " send the specified signal to the running process\n" " -c, --check check if the process is running\n" " (the above commands operate on dudki itself if no\n" " process name has been specified)\n" " -e, --ensure ensure that dudki is running\n" " -t, --test test configuration file and exit" #else /* !HAVE_GETOPT_LONG */ " -h display this text\n" " -V display version number\n" " -L show license\n" " -f filename specify the configuration file to use\n" "\n" " -k stop running instance (send SIGTERM)\n" " -r reload running instance (send SIGHUP)\n" + " -s signum send the specified signal to the running process\n" " -c check if the process is running\n" " (the above commands operate on dudki itself if no\n" " process name has been specified)\n" " -e ensure that dudki is running\n" " -t test configuration file and exit" #endif /* /HAVE_GETOPT_LONG */ << endl; exit(0); break; case 'V': cerr << VERSION << endl; exit(0); break; case 'L': extern const char *COPYING; cerr << COPYING << endl; exit(0); break; case 'f': config_file = optarg; break; case 'k': if(op!=op_default) { cerr << "Can't obey two or more orders at once" << endl; exit(1); } op = op_signal; op_signum = SIGTERM; break; case 'r': if(op!=op_default) { cerr << "Can't obey two or more orders at once" << endl; exit(1); } op = op_signal; op_signum = SIGHUP; break; case 'c': if(op!=op_default) { cerr << "Can't obey two or more orders at once" << endl; exit(1); } op = op_signal; op_signum = 0; break; case 'e': if(op!=op_default) { cerr << "Can't obey two or more orders at once" << endl; exit(1); } op = op_ensure; break; case 't': if(op!=op_default) { cerr << "Can't obey two or more orders at once" << endl; exit(1); } op = op_test; break; + case 's': + if(op!=op_default) { + cerr << "Can't obey two or more orders at once" << endl; + exit(1); + } + op = op_signal; + errno = 0; + op_signum = strtol(optarg,NULL,0); + if(errno) { + cerr << "Can't obtain the signal value" << endl; + exit(1); + } + break; default: cerr << "Huh??" << endl; exit(1); break; } } const char *sid = *argv; const char *t; while(t = index(sid,'/')) { sid = t; sid++; } openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); configuration config; config.parse(config_file); switch(op) { case op_test: cerr << "Configuration OK" << endl; break; case op_signal: try { if(optind>=argc) { signal_self(config,op_signum); }else{ int failures = 0; for(int narg=optind;narg<argc;narg++) { try { processes_t::const_iterator i = config.processes.find(argv[narg]); if(i==config.processes.end()) throw runtime_error("no such process configured"); i->second.signal(op_signum); }catch(exception& e) { cerr << "dudki(" << argv[narg] << "): " << e.what() << endl; failures++; } } if(failures) throw runtime_error("not all processes have been successfully signaled"); } if(!op_signum) exit(0); }catch(exception& e) { if(!op_signum) exit(1); } case op_ensure: try { signal_self(config,0); break; }catch(exception& e) { syslog(LOG_NOTICE,"The dudki process is down, taking its place"); config.daemonize = true; }catch(int zero) { // we throw zero in case we're ensuring that this very process is running. // we don't have to daemonize if we're daemonic. config.daemonize = false; } case op_default: case op_work: { if(config.daemonize) { pid_t pf = fork(); if(pf<0) throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); if(pf) { _exit(0); } } pid_file pidfile; pidfile.set(config.pidfile); signal(SIGINT,lethal_signal_handler); signal(SIGABRT,lethal_signal_handler); signal(SIGTERM,lethal_signal_handler); signal(SIGHUP,sighup_handler); sigset_t sset; sigemptyset(&sset); sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT); sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP); sigprocmask(SIG_UNBLOCK,&sset,NULL); while(!finishing) { check_herd(config); sleep(config.check_interval); } if(restarting) execvp(_argv[0],_argv); } break; default: throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); } }catch(exception& e) { cerr << "Oops: " << e.what() << endl; return 1; } } |