summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2004-07-23 20:40:46 (UTC)
committer Michael Krelin <hacker@klever.net>2004-07-23 20:40:46 (UTC)
commit546858a1e4d13d179a6af27b474e1396cfdf0c29 (patch) (unidiff)
treeac19b0ff5e4b3164ad5375bda112a9d6d2f88c2b
parent76921288a0aa39acb53102863523c388b5d0f9ee (diff)
downloaddudki-546858a1e4d13d179a6af27b474e1396cfdf0c29.zip
dudki-546858a1e4d13d179a6af27b474e1396cfdf0c29.tar.gz
dudki-546858a1e4d13d179a6af27b474e1396cfdf0c29.tar.bz2
the ability to check/kill/reload any of the processes being monitored added.
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--man/dudki.8.in11
-rw-r--r--src/dudki.cc55
-rw-r--r--src/process.cc30
-rw-r--r--src/process.h2
4 files changed, 62 insertions, 36 deletions
diff --git a/man/dudki.8.in b/man/dudki.8.in
index 3011034..05db733 100644
--- a/man/dudki.8.in
+++ b/man/dudki.8.in
@@ -1,123 +1,128 @@
1.TH dudki 8 "June 9th, 2004" "dudki(8)" "Klever Group (http://www.klever.net/)" 1.TH dudki 8 "June 9th, 2004" "dudki(8)" "Klever Group (http://www.klever.net/)"
2.hla en 2.hla en
3.ds longopt @HAVE_GETOPT_LONG@ 3.ds longopt @HAVE_GETOPT_LONG@
4 4
5.SH NAME 5.SH NAME
6 6
7dudki \- a process monitoring daemon 7dudki \- a process monitoring daemon
8 8
9.SH SYNOPSYS 9.SH SYNOPSYS
10 10
11\fBdudki\fR [\fB-h\fR] 11\fBdudki\fR [\fB-h\fR]
12.if \*[longopt] [\fB--help\fR] [\fB--usage\fR] 12.if \*[longopt] [\fB--help\fR] [\fB--usage\fR]
13[\fB-V\fR] 13[\fB-V\fR]
14.if \*[longopt] [\fB--version\fR] 14.if \*[longopt] [\fB--version\fR]
15[\fB-L\fR] 15[\fB-L\fR]
16.if \*[longopt] [\fB--license\fR] 16.if \*[longopt] [\fB--license\fR]
17[\fB-f\fR \fIconfigfile\fR] 17[\fB-f\fR \fIconfigfile\fR]
18.if \*[longopt] [\fB--config=\fR\fIconfigfile\fR] 18.if \*[longopt] [\fB--config=\fR\fIconfigfile\fR]
19[\fB-k\fR] 19[\fB-k\fR]
20.if \*[longopt] [\fB--kill\fR] 20.if \*[longopt] [\fB--kill\fR]
21[\fB-r\fR] 21[\fB-r\fR]
22.if \*[longopt] [\fB--reload\fR] 22.if \*[longopt] [\fB--reload\fR]
23[\fB-c\fR] 23[\fB-c\fR]
24.if \*[longopt] [\fB--check\fR] 24.if \*[longopt] [\fB--check\fR]
25[\fB-e\fR] 25[\fB-e\fR]
26.if \*[longopt] [\fB--ensure\fR] 26.if \*[longopt] [\fB--ensure\fR]
27[\fB-t\fR] 27[\fB-t\fR]
28.if \*[longopt] [\fB--test\fR] 28.if \*[longopt] [\fB--test\fR]
29[\fI<process-list>\fR]
29 30
30.SH DESCRIPTION 31.SH DESCRIPTION
31 32
32dudki daemon is designed to run in the background and periodically 33dudki daemon is designed to run in the background and periodically
33check if certain processes specified in the configuration file are 34check if certain processes specified in the configuration file are
34running. If a process is detected as dead dudki tries to restart it 35running. If a process is detected as dead dudki tries to restart it
35using the command line specified in the configuration file and notifies 36using the command line specified in the configuration file and notifies
36the specified contact (currently only via email). 37the specified contact (currently only via email).
37 38
38.SH OPTIONS 39.SH OPTIONS
39 40
40.TP 41.TP
41.ie \*[longopt] \fB-f\fR \fIconfigfile\fR, \fB--config=\fR\fIconfigfile\fR 42.ie \*[longopt] \fB-f\fR \fIconfigfile\fR, \fB--config=\fR\fIconfigfile\fR
42.el \fB-f\fR \fIconfigfile\fR 43.el \fB-f\fR \fIconfigfile\fR
43Specify the configuration file to use (default is 44Specify the configuration file to use (default is
44\fI@sysconfdir@/dudki.conf\fR). 45\fI@sysconfdir@/dudki.conf\fR).
45.TP 46.TP
46.ie \*[longopt] \fB-k\fR, \fB--kill\fR 47.ie \*[longopt] \fB-k\fR, \fB--kill\fR
47.el \fB-k\fR 48.el \fB-k\fR
48Stop the running instance by sending the \fBSIGTERM\fR signal. 49Stop the running instance by sending the \fBSIGTERM\fR signal. If no process
50name specified on the command line, dudki kills his own running instance.
49.TP 51.TP
50.ie \*[longopt] \fB-r\fR, \fB--reload\fR 52.ie \*[longopt] \fB-r\fR, \fB--reload\fR
51.el \fB-r\fR 53.el \fB-r\fR
52Reload the running instance by sending the \fBSIGHUP\fR signal. 54Reload the running instance by sending the \fBSIGHUP\fR signal. Like with
55\fB-k\fR, if no process name specified on the command line, dudki sends
56\fBSIGHUP\fR to his own running instance.
53.TP 57.TP
54.ie \*[longopt] \fB-c\fR, \fB--check\fR 58.ie \*[longopt] \fB-c\fR, \fB--check\fR
55.el \fB-c\fR 59.el \fB-c\fR
56Check if dudki is running. Exit with non-zero status if not. 60Check if dudki is running. Exit with non-zero status if not. The same target
61rules as in \fB-k\fR and \fB-r\fR apply here.
57.TP 62.TP
58.ie \*[longopt] \fB-e\fR, \fB--ensure\fR 63.ie \*[longopt] \fB-e\fR, \fB--ensure\fR
59.el \fB-e\fR 64.el \fB-e\fR
60Ensure that dudki is running. Load, if not. Useful for running as a 65Ensure that dudki is running. Load, if not. Useful for running as a
61cron job once in a while. If the daemon is running runs quietly 66cron job once in a while. If the daemon is running runs quietly
62providing no output. 67providing no output.
63.TP 68.TP
64.ie \*[longopt] \fB-t\fR, \fB--test\fR 69.ie \*[longopt] \fB-t\fR, \fB--test\fR
65.el \fB-t\fR 70.el \fB-t\fR
66Check the syntax of configuration file and exit. 71Check the syntax of configuration file and exit.
67.TP 72.TP
68.ie \*[longopt] \fB-h\fR, \fB--help\fR, \fB--usage\fR 73.ie \*[longopt] \fB-h\fR, \fB--help\fR, \fB--usage\fR
69.el \fB-h\fR 74.el \fB-h\fR
70Display short usage message and exit. 75Display short usage message and exit.
71.TP 76.TP
72.ie \*[longopt] \fB-V\fR, \fB--version\fR 77.ie \*[longopt] \fB-V\fR, \fB--version\fR
73.el \fB-V\fR 78.el \fB-V\fR
74Report version and exit. 79Report version and exit.
75.TP 80.TP
76.ie \*[longopt] \fB-L\fR, \fB--license\fR 81.ie \*[longopt] \fB-L\fR, \fB--license\fR
77.el \fB-L\fR 82.el \fB-L\fR
78Show licensing terms. 83Show licensing terms.
79 84
80.SH EXIT STATUS 85.SH EXIT STATUS
81 86
82Zero in case of success, non-zero otherwise. 87Zero in case of success, non-zero otherwise.
83 88
84.SH FILES 89.SH FILES
85.TP 90.TP
86@sysconfdir@/dudki.conf 91@sysconfdir@/dudki.conf
87Default configuration file. 92Default configuration file.
88.TP 93.TP
89/var/run/dudki.pid 94/var/run/dudki.pid
90Default pid-file for the dudki process. 95Default pid-file for the dudki process.
91 96
92.SH AUTHOR 97.SH AUTHOR
93 98
94Written by Michael 'hacker' Krelin <hacker@klever.net> 99Written by Michael 'hacker' Krelin <hacker@klever.net>
95 100
96.SH COPYRIGHT 101.SH COPYRIGHT
97 102
98Copyright (c) 2004 Klever Group (http://www.klever.net/) 103Copyright (c) 2004 Klever Group (http://www.klever.net/)
99 104
100Permission is hereby granted, free of charge, to any person obtaining a copy of 105Permission is hereby granted, free of charge, to any person obtaining a copy of
101this software and associated documentation files (the "Software"), to deal in 106this software and associated documentation files (the "Software"), to deal in
102the Software without restriction, including without limitation the rights to 107the Software without restriction, including without limitation the rights to
103use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 108use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
104of the Software, and to permit persons to whom the Software is furnished to do 109of the Software, and to permit persons to whom the Software is furnished to do
105so, subject to the following conditions: 110so, subject to the following conditions:
106 111
107The above copyright notice and this permission notice shall be included in all 112The above copyright notice and this permission notice shall be included in all
108copies or substantial portions of the Software. 113copies or substantial portions of the Software.
109 114
110THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 115THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
111IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 116IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
112FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 117FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
113AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 118AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
114LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 119LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
115OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 120OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
116SOFTWARE. 121SOFTWARE.
117 122
118.SH BUGS 123.SH BUGS
119 124
120You tell me. Send reports to <dudki-bugs@klever.net> 125You tell me. Send reports to <dudki-bugs@klever.net>
121 126
122.SH SEE ALSO 127.SH SEE ALSO
123\fBdudki.conf\fR(5) 128\fBdudki.conf\fR(5)
diff --git a/src/dudki.cc b/src/dudki.cc
index b4e95a7..e91ad5e 100644
--- a/src/dudki.cc
+++ b/src/dudki.cc
@@ -1,252 +1,269 @@
1#include <unistd.h> 1#include <unistd.h>
2#include <signal.h> 2#include <signal.h>
3#include <syslog.h> 3#include <syslog.h>
4#include <iostream> 4#include <iostream>
5#include <fstream> 5#include <fstream>
6#include <stdexcept> 6#include <stdexcept>
7using namespace std; 7using namespace std;
8#include "configuration.h" 8#include "configuration.h"
9#include "util.h" 9#include "util.h"
10 10
11#include "config.h" 11#include "config.h"
12#ifdef HAVE_GETOPT_H 12#ifdef HAVE_GETOPT_H
13# include <getopt.h> 13# include <getopt.h>
14#endif 14#endif
15 15
16#ifndef DEFAULT_CONF_FILE 16#ifndef DEFAULT_CONF_FILE
17# define DEFAULT_CONF_FILE "/etc/dudki.conf" 17# define DEFAULT_CONF_FILE "/etc/dudki.conf"
18#endif 18#endif
19 19
20#define PHEADER PACKAGE " Version " VERSION 20#define PHEADER PACKAGE " Version " VERSION
21#define PCOPY "Copyright (c) 2004 Klever Group" 21#define PCOPY "Copyright (c) 2004 Klever Group"
22 22
23bool finishing = false; 23bool finishing = false;
24bool restarting = false; 24bool restarting = false;
25static char **_argv = NULL; 25static char **_argv = NULL;
26 26
27static void lethal_signal_handler(int signum) { 27static void lethal_signal_handler(int signum) {
28 syslog(LOG_NOTICE,"Lethal signal received. Terminating."); 28 syslog(LOG_NOTICE,"Lethal signal received. Terminating.");
29 finishing = true; 29 finishing = true;
30} 30}
31static void sighup_handler(int signum) { 31static void sighup_handler(int signum) {
32 syslog(LOG_NOTICE,"SUGHUP received, reloading."); 32 syslog(LOG_NOTICE,"SUGHUP received, reloading.");
33 restarting = finishing = true; 33 restarting = finishing = true;
34} 34}
35 35
36void check_herd(configuration& config) { 36void check_herd(configuration& config) {
37 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) 37 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i)
38 i->second.check(i->first,config); 38 i->second.check(i->first,config);
39} 39}
40 40
41void signal_self(const configuration& config,int signum) { 41void signal_self(const configuration& config,int signum) {
42 ifstream pids(config.pidfile.c_str(),ios::in); 42 ifstream pids(config.pidfile.c_str(),ios::in);
43 if(!pids) 43 if(!pids)
44 throw runtime_error("Can't detect running instance"); 44 throw runtime_error("Can't detect running instance");
45 pid_t pid = 0; 45 pid_t pid = 0;
46 pids >> pid; 46 pids >> pid;
47 if(!pid) 47 if(!pid)
48 throw runtime_error("Can't detect running instance"); 48 throw runtime_error("Can't detect running instance");
49 if(pid==getpid()) 49 if(pid==getpid())
50 throw 0; 50 throw 0;
51 if(kill(pid,signum)) 51 if(kill(pid,signum))
52 throw runtime_error("Failed to signal running instance"); 52 throw runtime_error("Failed to signal running instance");
53} 53}
54 54
55int main(int argc,char **argv) { 55int main(int argc,char **argv) {
56 try { 56 try {
57 _argv = new char*[argc+1]; 57 _argv = new char*[argc+1];
58 if(!_argv) 58 if(!_argv)
59 throw runtime_error("memory allocation problem at the very start"); 59 throw runtime_error("memory allocation problem at the very start");
60 memmove(_argv,argv,sizeof(*_argv)*(argc+1)); 60 memmove(_argv,argv,sizeof(*_argv)*(argc+1));
61 string config_file = DEFAULT_CONF_FILE; 61 string config_file = DEFAULT_CONF_FILE;
62 enum { 62 enum {
63 op_default, 63 op_default,
64 op_work, 64 op_work,
65 op_hup, 65 op_signal,
66 op_term,
67 op_check,
68 op_ensure, 66 op_ensure,
69 op_test 67 op_test
70 } op = op_default; 68 } op = op_default;
69 int op_signum = 0;
71 while(true) { 70 while(true) {
72 #defineSHORTOPTSTRING "f:hVLrkcet" 71 #defineSHORTOPTSTRING "f:hVLrkcet"
73#ifdef HAVE_GETOPT_LONG 72#ifdef HAVE_GETOPT_LONG
74 static struct option opts[] = { 73 static struct option opts[] = {
75 { "help", no_argument, 0, 'h' }, 74 { "help", no_argument, 0, 'h' },
76 { "usage", no_argument, 0, 'h' }, 75 { "usage", no_argument, 0, 'h' },
77 { "version", no_argument, 0, 'V' }, 76 { "version", no_argument, 0, 'V' },
78 { "license", no_argument, 0, 'L' }, 77 { "license", no_argument, 0, 'L' },
79 { "config", required_argument, 0, 'f' }, 78 { "config", required_argument, 0, 'f' },
80 { "kill", no_argument, 0, 'k' }, 79 { "kill", no_argument, 0, 'k' },
81 { "reload", no_argument, 0, 'r' }, 80 { "reload", no_argument, 0, 'r' },
82 { "check", no_argument, 0, 'c' }, 81 { "check", no_argument, 0, 'c' },
83 { "ensure", no_argument, 0, 'e' }, 82 { "ensure", no_argument, 0, 'e' },
84 { "test", no_argument, 0, 't' }, 83 { "test", no_argument, 0, 't' },
85 { NULL, 0, 0, 0 } 84 { NULL, 0, 0, 0 }
86 }; 85 };
87 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); 86 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL);
88#else /* !HAVE_GETOPT_LONG */ 87#else /* !HAVE_GETOPT_LONG */
89 int c = getopt(argc,argv,SHORTOPTSTRING); 88 int c = getopt(argc,argv,SHORTOPTSTRING);
90#endif /* /HAVE_GETOPT_LONG */ 89#endif /* /HAVE_GETOPT_LONG */
91 if(c==-1) 90 if(c==-1)
92 break; 91 break;
93 switch(c) { 92 switch(c) {
94 case 'h': 93 case 'h':
95 cerr << PHEADER << endl 94 cerr << PHEADER << endl
96 << PCOPY << endl << endl << 95 << PCOPY << endl << endl
96 << " " << argv[0] << " [options] [processes]" << endl << endl <<
97#ifdef HAVE_GETOPT_LONG 97#ifdef HAVE_GETOPT_LONG
98 " -h, --help\n" 98 " -h, --help\n"
99 " --usage display this text\n" 99 " --usage display this text\n"
100 " -V, --version display version number\n" 100 " -V, --version display version number\n"
101 " -L, --license show license\n" 101 " -L, --license show license\n"
102 " -f filename, --config=filename\n" 102 " -f filename, --config=filename\n"
103 " specify the configuration file to use\n" 103 " specify the configuration file to use\n"
104 "\n" 104 "\n"
105 " -k, --kill stop running instance\n" 105 " -k, --kill stop running instance (send SIGTERM)\n"
106 " -r, --reload reload running instance (send SIGHUP)\n" 106 " -r, --reload reload running instance (send SIGHUP)\n"
107 " -c, --check check if dudki is running\n" 107 " -c, --check check if the process is running\n"
108 " (the above commands operate on dudki itself if no\n"
109 " process name has been specified)\n"
108 " -e, --ensure ensure that dudki is running\n" 110 " -e, --ensure ensure that dudki is running\n"
109 " -t, --test test configuration file and exit" 111 " -t, --test test configuration file and exit"
110#else /* !HAVE_GETOPT_LONG */ 112#else /* !HAVE_GETOPT_LONG */
111 " -h display this text\n" 113 " -h display this text\n"
112 " -V display version number\n" 114 " -V display version number\n"
113 " -L show license\n" 115 " -L show license\n"
114 " -f filename specify the configuration file to use\n" 116 " -f filename specify the configuration file to use\n"
115 "\n" 117 "\n"
116 " -k stop running instance\n" 118 " -k stop running instance (send SIGTERM)\n"
117 " -r reload running instance (send SIGHUP)\n" 119 " -r reload running instance (send SIGHUP)\n"
118 " -c check if dudki is running\n" 120 " -c check if the process is running\n"
121 " (the above commands operate on dudki itself if no\n"
122 " process name has been specified)\n"
119 " -e ensure that dudki is running\n" 123 " -e ensure that dudki is running\n"
120 " -t test configuration file and exit" 124 " -t test configuration file and exit"
121#endif /* /HAVE_GETOPT_LONG */ 125#endif /* /HAVE_GETOPT_LONG */
122 << endl; 126 << endl;
123 exit(0); 127 exit(0);
124 break; 128 break;
125 case 'V': 129 case 'V':
126 cerr << VERSION << endl; 130 cerr << VERSION << endl;
127 exit(0); 131 exit(0);
128 break; 132 break;
129 case 'L': 133 case 'L':
130 extern const char *COPYING; 134 extern const char *COPYING;
131 cerr << COPYING << endl; 135 cerr << COPYING << endl;
132 exit(0); 136 exit(0);
133 break; 137 break;
134 case 'f': 138 case 'f':
135 config_file = optarg; 139 config_file = optarg;
136 break; 140 break;
137 case 'k': 141 case 'k':
138 if(op!=op_default) { 142 if(op!=op_default) {
139 cerr << "Can't obey two or more orders at once" << endl; 143 cerr << "Can't obey two or more orders at once" << endl;
140 exit(1); 144 exit(1);
141 } 145 }
142 op = op_term; 146 op = op_signal; op_signum = SIGTERM;
143 break; 147 break;
144 case 'r': 148 case 'r':
145 if(op!=op_default) { 149 if(op!=op_default) {
146 cerr << "Can't obey two or more orders at once" << endl; 150 cerr << "Can't obey two or more orders at once" << endl;
147 exit(1); 151 exit(1);
148 } 152 }
149 op = op_hup; 153 op = op_signal; op_signum = SIGHUP;
150 break; 154 break;
151 case 'c': 155 case 'c':
152 if(op!=op_default) { 156 if(op!=op_default) {
153 cerr << "Can't obey two or more orders at once" << endl; 157 cerr << "Can't obey two or more orders at once" << endl;
154 exit(1); 158 exit(1);
155 } 159 }
156 op = op_check; 160 op = op_signal; op_signum = 0;
157 break; 161 break;
158 case 'e': 162 case 'e':
159 if(op!=op_default) { 163 if(op!=op_default) {
160 cerr << "Can't obey two or more orders at once" << endl; 164 cerr << "Can't obey two or more orders at once" << endl;
161 exit(1); 165 exit(1);
162 } 166 }
163 op = op_ensure; 167 op = op_ensure;
164 break; 168 break;
165 case 't': 169 case 't':
166 if(op!=op_default) { 170 if(op!=op_default) {
167 cerr << "Can't obey two or more orders at once" << endl; 171 cerr << "Can't obey two or more orders at once" << endl;
168 exit(1); 172 exit(1);
169 } 173 }
170 op = op_test; 174 op = op_test;
171 break; 175 break;
172 default: 176 default:
173 cerr << "Huh??" << endl; 177 cerr << "Huh??" << endl;
174 exit(1); 178 exit(1);
175 break; 179 break;
176 } 180 }
177 } 181 }
178 const char *sid = *argv; 182 const char *sid = *argv;
179 const char *t; 183 const char *t;
180 while(t = index(sid,'/')) { 184 while(t = index(sid,'/')) {
181 sid = t; sid++; 185 sid = t; sid++;
182 } 186 }
183 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); 187 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON);
184 configuration config; 188 configuration config;
185 config.parse(config_file); 189 config.parse(config_file);
186 switch(op) { 190 switch(op) {
187 case op_test: 191 case op_test:
188 cerr << "Configuration OK" << endl; 192 cerr << "Configuration OK" << endl;
189 break; 193 break;
190 case op_hup: 194 case op_signal:
191 signal_self(config,SIGHUP);
192 break;
193 case op_term:
194 signal_self(config,SIGTERM);
195 break;
196 case op_check:
197 try{ 195 try{
198 signal_self(config,0); 196 if(optind>=argc) {
197 signal_self(config,op_signum);
198 }else{
199 int failures = 0;
200 for(int narg=optind;narg<argc;narg++) {
201 try {
202 processes_t::const_iterator i = config.processes.find(argv[narg]);
203 if(i==config.processes.end())
204 throw runtime_error("no such process configured");
205 i->second.signal(op_signum);
206 }catch(exception& e) {
207 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl;
208 failures++;
209 }
210 }
211 if(failures)
212 throw runtime_error("not all processes have been successfully signaled");
213 }
214 if(!op_signum)
199 exit(0); 215 exit(0);
200 }catch(exception& e) { 216 }catch(exception& e) {
217 if(!op_signum)
201 exit(1); 218 exit(1);
202 } 219 }
203 case op_ensure: 220 case op_ensure:
204 try { 221 try {
205 signal_self(config,0); 222 signal_self(config,0);
206 break; 223 break;
207 }catch(exception& e) { 224 }catch(exception& e) {
208 syslog(LOG_NOTICE,"The dudki process is down, taking its place"); 225 syslog(LOG_NOTICE,"The dudki process is down, taking its place");
209 config.daemonize = true; 226 config.daemonize = true;
210 }catch(int zero) { 227 }catch(int zero) {
211 // we throw zero in case we're ensuring that this very process is running. 228 // we throw zero in case we're ensuring that this very process is running.
212 // we don't have to daemonize if we're daemonic. 229 // we don't have to daemonize if we're daemonic.
213 config.daemonize = false; 230 config.daemonize = false;
214 } 231 }
215 case op_default: 232 case op_default:
216 case op_work: 233 case op_work:
217 { 234 {
218 if(config.daemonize) { 235 if(config.daemonize) {
219 pid_t pf = fork(); 236 pid_t pf = fork();
220 if(pf<0) 237 if(pf<0)
221 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 238 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
222 if(pf) { 239 if(pf) {
223 _exit(0); 240 _exit(0);
224 } 241 }
225 } 242 }
226 pid_file pidfile; 243 pid_file pidfile;
227 pidfile.set(config.pidfile); 244 pidfile.set(config.pidfile);
228 signal(SIGINT,lethal_signal_handler); 245 signal(SIGINT,lethal_signal_handler);
229 signal(SIGABRT,lethal_signal_handler); 246 signal(SIGABRT,lethal_signal_handler);
230 signal(SIGTERM,lethal_signal_handler); 247 signal(SIGTERM,lethal_signal_handler);
231 signal(SIGHUP,sighup_handler); 248 signal(SIGHUP,sighup_handler);
232 sigset_t sset; 249 sigset_t sset;
233 sigemptyset(&sset); 250 sigemptyset(&sset);
234 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT); 251 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT);
235 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP); 252 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP);
236 sigprocmask(SIG_UNBLOCK,&sset,NULL); 253 sigprocmask(SIG_UNBLOCK,&sset,NULL);
237 while(!finishing) { 254 while(!finishing) {
238 check_herd(config); 255 check_herd(config);
239 sleep(config.check_interval); 256 sleep(config.check_interval);
240 } 257 }
241 if(restarting) 258 if(restarting)
242 execvp(_argv[0],_argv); 259 execvp(_argv[0],_argv);
243 } 260 }
244 break; 261 break;
245 default: 262 default:
246 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); 263 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error");
247 } 264 }
248 }catch(exception& e) { 265 }catch(exception& e) {
249 cerr << "Oops: " << e.what() << endl; 266 cerr << "Oops: " << e.what() << endl;
250 return 1; 267 return 1;
251 } 268 }
252} 269}
diff --git a/src/process.cc b/src/process.cc
index bfab311..1ffac9f 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,187 +1,189 @@
1#include <stdio.h> 1#include <stdio.h>
2#include <sys/types.h> 2#include <sys/types.h>
3#include <unistd.h> 3#include <unistd.h>
4#include <signal.h> 4#include <signal.h>
5#include <pwd.h> 5#include <pwd.h>
6#include <grp.h> 6#include <grp.h>
7#include <sys/wait.h> 7#include <sys/wait.h>
8#include <syslog.h> 8#include <syslog.h>
9#include <errno.h> 9#include <errno.h>
10#include <iostream> 10#include <iostream>
11#include <fstream> 11#include <fstream>
12#include <stdexcept> 12#include <stdexcept>
13using namespace std; 13using namespace std;
14#include "process.h" 14#include "process.h"
15#include "configuration.h" 15#include "configuration.h"
16 16
17void process::check(const string& id,configuration& config) { 17void process::check(const string& id,configuration& config) {
18 bool running = false; 18 try {
19 ifstream pids(pidfile.c_str(),ios::in); 19 signal(0);
20 if(pids) {
21 pid_t pid = 0;
22 pids >> pid;
23 pids.close();
24 if(pid) {
25 if(!kill(pid,0)) {
26 running = true;
27 }
28 }
29 }
30 if(running){
31 patience = 0; 20 patience = 0;
32 }else{ 21 }catch(exception& e) {
33 if(patience>60) { // TODO: configurable 22 if(patience>60) { // TODO: configurable
34 patience = 0; 23 patience = 0;
35 }else{ 24 }else{
36 if(patience<10) { // TODO: configurable 25 if(patience<10) { // TODO: configurable
37 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str()); 26 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
38 do_notify(id,"Starting up", 27 do_notify(id,"Starting up",
39 "The named process seems to be down. Dudki will try\n" 28 "The named process seems to be down. Dudki will try\n"
40 "to revive it by running the specified command.\n", 29 "to revive it by running the specified command.\n",
41 config); 30 config);
42 try { 31 try {
43 launch(id,config); 32 launch(id,config);
44 }catch(exception& e) { 33 }catch(exception& e) {
45 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 34 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
46 } 35 }
47 }else if(patience==10){ // TODO: configurable like the above 36 }else if(patience==10){ // TODO: configurable like the above
48 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str()); 37 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
49 do_notify(id,"Giving up", 38 do_notify(id,"Giving up",
50 "After a number of attempts to relaunch the named process\n" 39 "After a number of attempts to relaunch the named process\n"
51 "It still seems to be down. Dudki is giving up attempts\n" 40 "It still seems to be down. Dudki is giving up attempts\n"
52 "to revive the process for a while.\n", 41 "to revive the process for a while.\n",
53 config); 42 config);
54 } 43 }
55 patience++; 44 patience++;
56 } 45 }
57 } 46 }
58} 47}
59 48
60void process::launch(const string& id,configuration& config) { 49void process::launch(const string& id,configuration& config) {
61 uid_t uid = (uid_t)-1; 50 uid_t uid = (uid_t)-1;
62 gid_t gid = (gid_t)-1; 51 gid_t gid = (gid_t)-1;
63 if(!user.empty()) { 52 if(!user.empty()) {
64 struct passwd *ptmp = getpwnam(user.c_str()); 53 struct passwd *ptmp = getpwnam(user.c_str());
65 if(ptmp) { 54 if(ptmp) {
66 uid = ptmp->pw_uid; 55 uid = ptmp->pw_uid;
67 gid = ptmp->pw_gid; 56 gid = ptmp->pw_gid;
68 }else{ 57 }else{
69 errno=0; 58 errno=0;
70 uid = strtol(user.c_str(),NULL,0); 59 uid = strtol(user.c_str(),NULL,0);
71 if(errno) 60 if(errno)
72 throw runtime_error("Failed to resolve User value to uid"); 61 throw runtime_error("Failed to resolve User value to uid");
73 } 62 }
74 } 63 }
75 if(!group.empty()) { 64 if(!group.empty()) {
76 struct group *gtmp = getgrnam(group.c_str()); 65 struct group *gtmp = getgrnam(group.c_str());
77 if(gtmp) { 66 if(gtmp) {
78 gid = gtmp->gr_gid; 67 gid = gtmp->gr_gid;
79 }else{ 68 }else{
80 errno = 0; 69 errno = 0;
81 gid = strtol(group.c_str(),NULL,0); 70 gid = strtol(group.c_str(),NULL,0);
82 if(errno) 71 if(errno)
83 throw runtime_error("Failed to reslove Group value to gid"); 72 throw runtime_error("Failed to reslove Group value to gid");
84 } 73 }
85 } 74 }
86 pid_t p = fork(); 75 pid_t p = fork();
87 if(p<0) 76 if(p<0)
88 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 77 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
89 if(!p) { 78 if(!p) {
90 // child 79 // child
91 try { 80 try {
92 setsid(); 81 setsid();
93 if(user.empty()) { 82 if(user.empty()) {
94 if((getgid()!=gid) && setgid(gid)) 83 if((getgid()!=gid) && setgid(gid))
95 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); 84 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
96 }else{ 85 }else{
97 if(initgroups(user.c_str(),gid)) 86 if(initgroups(user.c_str(),gid))
98 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()"); 87 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()");
99 } 88 }
100 if(!chroot.empty()) { 89 if(!chroot.empty()) {
101 if(::chroot(chroot.c_str())) 90 if(::chroot(chroot.c_str()))
102 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); 91 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
103 } 92 }
104 if(!user.empty()) { 93 if(!user.empty()) {
105 if((getuid()!=uid) && setuid(uid)) 94 if((getuid()!=uid) && setuid(uid))
106 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); 95 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
107 } 96 }
108 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; 97 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
109 close(0); close(1); close(2); 98 close(0); close(1); close(2);
110 execv("/bin/sh",argv); 99 execv("/bin/sh",argv);
111 }catch(exception& e) { 100 }catch(exception& e) {
112 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 101 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
113 } 102 }
114 _exit(-1); 103 _exit(-1);
115 } 104 }
116 // parent 105 // parent
117 int rv; 106 int rv;
118 if(waitpid(p,&rv,0)<0) 107 if(waitpid(p,&rv,0)<0)
119 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); 108 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
120} 109}
121 110
122void process::do_notify(const string& id,const string& event,const string& description,configuration& config) { 111void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
123 string the_notify; 112 string the_notify;
124 if(!notify.empty()) 113 if(!notify.empty())
125 the_notify=notify; 114 the_notify=notify;
126 else if(!config.notify.empty()) 115 else if(!config.notify.empty())
127 the_notify=config.notify; 116 the_notify=config.notify;
128 else 117 else
129 return; 118 return;
130 try { 119 try {
131 string::size_type colon = the_notify.find(':'); 120 string::size_type colon = the_notify.find(':');
132 if(colon==string::npos) 121 if(colon==string::npos)
133 throw runtime_error("invalid notify action specification"); 122 throw runtime_error("invalid notify action specification");
134 string nschema = the_notify.substr(0,colon); 123 string nschema = the_notify.substr(0,colon);
135 string ntarget = the_notify.substr(colon+1); 124 string ntarget = the_notify.substr(colon+1);
136 if(nschema=="mailto") { 125 if(nschema=="mailto") {
137 notify_mailto(ntarget,id,event,description,config); 126 notify_mailto(ntarget,id,event,description,config);
138 }else 127 }else
139 throw runtime_error("unrecognized notification schema"); 128 throw runtime_error("unrecognized notification schema");
140 }catch(exception& e) { 129 }catch(exception& e) {
141 syslog(LOG_ERR,"Notification error: %s",e.what()); 130 syslog(LOG_ERR,"Notification error: %s",e.what());
142 } 131 }
143} 132}
144 133
145void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) { 134void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
146 int files[2]; 135 int files[2];
147 if(pipe(files)) 136 if(pipe(files))
148 throw runtime_error("Failed to pipe()"); 137 throw runtime_error("Failed to pipe()");
149 pid_t pid = vfork(); 138 pid_t pid = vfork();
150 if(pid==-1) { 139 if(pid==-1) {
151 close(files[0]); 140 close(files[0]);
152 close(files[1]); 141 close(files[1]);
153 throw runtime_error("Failed to vfork()"); 142 throw runtime_error("Failed to vfork()");
154 } 143 }
155 if(!pid) { 144 if(!pid) {
156 // child 145 // child
157 if(dup2(files[0],0)!=0) 146 if(dup2(files[0],0)!=0)
158 _exit(-1); 147 _exit(-1);
159 close(1); 148 close(1);
160 close(files[0]); 149 close(files[0]);
161 close(files[1]); 150 close(files[1]);
162 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL); 151 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
163 _exit(-1); 152 _exit(-1);
164 } 153 }
165 // parent 154 // parent
166 close(files[0]); 155 close(files[0]);
167 FILE *mta = fdopen(files[1],"w"); 156 FILE *mta = fdopen(files[1],"w");
168 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { 157 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
169 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 158 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
170 } 159 }
171 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { 160 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
172 if(mailto_headers.find(i->first)!=mailto_headers.end()) 161 if(mailto_headers.find(i->first)!=mailto_headers.end())
173 continue; 162 continue;
174 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 163 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
175 } 164 }
176 fprintf(mta, 165 fprintf(mta,
177 "Subject: [%s] %s\n\n" 166 "Subject: [%s] %s\n\n"
178 "%s\n" 167 "%s\n"
179 "---\n" 168 "---\n"
180 "This message was sent automatically by the 'dudki' daemon\n", 169 "This message was sent automatically by the 'dudki' daemon\n",
181 id.c_str(), event.c_str(), 170 id.c_str(), event.c_str(),
182 description.c_str() ); 171 description.c_str() );
183 fclose(mta); 172 fclose(mta);
184 int status; 173 int status;
185 waitpid(pid,&status,0); 174 waitpid(pid,&status,0);
186 // TODO: check the return code 175 // TODO: check the return code
187} 176}
177
178void process::signal(int signum) const {
179 ifstream pids(pidfile.c_str(),ios::in);
180 if(!pids)
181 throw runtime_error("no pidfile found");
182 pid_t pid = 0;
183 pids >> pid;
184 pids.close();
185 if(!pid)
186 throw runtime_error("no pid in pidfile");
187 if(kill(pid,signum))
188 throw runtime_error("failed to signal process");
189}
diff --git a/src/process.h b/src/process.h
index b6d7091..27ee049 100644
--- a/src/process.h
+++ b/src/process.h
@@ -1,36 +1,38 @@
1#ifndef __PROCESS_H 1#ifndef __PROCESS_H
2#define __PROCESS_H 2#define __PROCESS_H
3 3
4#include <string> 4#include <string>
5#include <map> 5#include <map>
6using namespace std; 6using namespace std;
7 7
8class configuration; 8class configuration;
9 9
10typedef map<string,string> headers_t; 10typedef map<string,string> headers_t;
11 11
12class process { 12class process {
13 public: 13 public:
14 string pidfile; 14 string pidfile;
15 string restart_cmd; 15 string restart_cmd;
16 string notify; 16 string notify;
17 string user; 17 string user;
18 string group; 18 string group;
19 string chroot; 19 string chroot;
20 headers_t mailto_headers; 20 headers_t mailto_headers;
21 21
22 int patience; 22 int patience;
23 23
24 process() 24 process()
25 : patience(0) { } 25 : patience(0) { }
26 26
27 void check(const string& id,configuration& config); 27 void check(const string& id,configuration& config);
28 void launch(const string& id,configuration& config); 28 void launch(const string& id,configuration& config);
29 void do_notify(const string& id,const string& event,const string& description,configuration& config); 29 void do_notify(const string& id,const string& event,const string& description,configuration& config);
30 void notify_mailto(const string& email,const string& id,const string& event, 30 void notify_mailto(const string& email,const string& id,const string& event,
31 const string& description,configuration& config); 31 const string& description,configuration& config);
32
33 void signal(int signum) const;
32}; 34};
33 35
34typedef map<string,process> processes_t; 36typedef map<string,process> processes_t;
35 37
36#endif /* __PROCESS_H */ 38#endif /* __PROCESS_H */