summaryrefslogtreecommitdiffabout
path: root/src
Unidiff
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/dudki.cc20
1 files changed, 19 insertions, 1 deletions
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 @@
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 <errno.h>
4#include <iostream> 5#include <iostream>
5#include <fstream> 6#include <fstream>
6#include <stdexcept> 7#include <stdexcept>
7using namespace std; 8using namespace std;
8#include "configuration.h" 9#include "configuration.h"
9#include "util.h" 10#include "util.h"
10 11
11#include "config.h" 12#include "config.h"
12#ifdef HAVE_GETOPT_H 13#ifdef HAVE_GETOPT_H
13# include <getopt.h> 14# include <getopt.h>
14#endif 15#endif
15 16
16#ifndef DEFAULT_CONF_FILE 17#ifndef DEFAULT_CONF_FILE
17# define DEFAULT_CONF_FILE "/etc/dudki.conf" 18# define DEFAULT_CONF_FILE "/etc/dudki.conf"
18#endif 19#endif
19 20
20#define PHEADER PACKAGE " Version " VERSION 21#define PHEADER PACKAGE " Version " VERSION
21#define PCOPY "Copyright (c) 2004 Klever Group" 22#define PCOPY "Copyright (c) 2004 Klever Group"
22 23
23bool finishing = false; 24bool finishing = false;
24bool restarting = false; 25bool restarting = false;
25static char **_argv = NULL; 26static char **_argv = NULL;
26 27
27static void lethal_signal_handler(int signum) { 28static void lethal_signal_handler(int signum) {
28 syslog(LOG_NOTICE,"Lethal signal received. Terminating."); 29 syslog(LOG_NOTICE,"Lethal signal received. Terminating.");
29 finishing = true; 30 finishing = true;
30} 31}
31static void sighup_handler(int signum) { 32static void sighup_handler(int signum) {
32 syslog(LOG_NOTICE,"SUGHUP received, reloading."); 33 syslog(LOG_NOTICE,"SUGHUP received, reloading.");
33 restarting = finishing = true; 34 restarting = finishing = true;
34} 35}
35 36
36void check_herd(configuration& config) { 37void check_herd(configuration& config) {
37 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) 38 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i)
38 i->second.check(i->first,config); 39 i->second.check(i->first,config);
39} 40}
40 41
41void signal_self(const configuration& config,int signum) { 42void signal_self(const configuration& config,int signum) {
42 ifstream pids(config.pidfile.c_str(),ios::in); 43 ifstream pids(config.pidfile.c_str(),ios::in);
43 if(!pids) 44 if(!pids)
44 throw runtime_error("Can't detect running instance"); 45 throw runtime_error("Can't detect running instance");
45 pid_t pid = 0; 46 pid_t pid = 0;
46 pids >> pid; 47 pids >> pid;
47 if(!pid) 48 if(!pid)
48 throw runtime_error("Can't detect running instance"); 49 throw runtime_error("Can't detect running instance");
49 if(pid==getpid()) 50 if(pid==getpid())
50 throw 0; 51 throw 0;
51 if(kill(pid,signum)) 52 if(kill(pid,signum))
52 throw runtime_error("Failed to signal running instance"); 53 throw runtime_error("Failed to signal running instance");
53} 54}
54 55
55int main(int argc,char **argv) { 56int main(int argc,char **argv) {
56 try { 57 try {
57 _argv = new char*[argc+1]; 58 _argv = new char*[argc+1];
58 if(!_argv) 59 if(!_argv)
59 throw runtime_error("memory allocation problem at the very start"); 60 throw runtime_error("memory allocation problem at the very start");
60 memmove(_argv,argv,sizeof(*_argv)*(argc+1)); 61 memmove(_argv,argv,sizeof(*_argv)*(argc+1));
61 string config_file = DEFAULT_CONF_FILE; 62 string config_file = DEFAULT_CONF_FILE;
62 enum { 63 enum {
63 op_default, 64 op_default,
64 op_work, 65 op_work,
65 op_signal, 66 op_signal,
66 op_ensure, 67 op_ensure,
67 op_test 68 op_test
68 } op = op_default; 69 } op = op_default;
69 int op_signum = 0; 70 int op_signum = 0;
70 while(true) { 71 while(true) {
71 #defineSHORTOPTSTRING "f:hVLrkcet" 72 #defineSHORTOPTSTRING "f:hVLrkcets:"
72#ifdef HAVE_GETOPT_LONG 73#ifdef HAVE_GETOPT_LONG
73 static struct option opts[] = { 74 static struct option opts[] = {
74 { "help", no_argument, 0, 'h' }, 75 { "help", no_argument, 0, 'h' },
75 { "usage", no_argument, 0, 'h' }, 76 { "usage", no_argument, 0, 'h' },
76 { "version", no_argument, 0, 'V' }, 77 { "version", no_argument, 0, 'V' },
77 { "license", no_argument, 0, 'L' }, 78 { "license", no_argument, 0, 'L' },
78 { "config", required_argument, 0, 'f' }, 79 { "config", required_argument, 0, 'f' },
79 { "kill", no_argument, 0, 'k' }, 80 { "kill", no_argument, 0, 'k' },
80 { "reload", no_argument, 0, 'r' }, 81 { "reload", no_argument, 0, 'r' },
82 { "signal", required_argument, 0, 's' },
81 { "check", no_argument, 0, 'c' }, 83 { "check", no_argument, 0, 'c' },
82 { "ensure", no_argument, 0, 'e' }, 84 { "ensure", no_argument, 0, 'e' },
83 { "test", no_argument, 0, 't' }, 85 { "test", no_argument, 0, 't' },
84 { NULL, 0, 0, 0 } 86 { NULL, 0, 0, 0 }
85 }; 87 };
86 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); 88 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL);
87#else /* !HAVE_GETOPT_LONG */ 89#else /* !HAVE_GETOPT_LONG */
88 int c = getopt(argc,argv,SHORTOPTSTRING); 90 int c = getopt(argc,argv,SHORTOPTSTRING);
89#endif /* /HAVE_GETOPT_LONG */ 91#endif /* /HAVE_GETOPT_LONG */
90 if(c==-1) 92 if(c==-1)
91 break; 93 break;
92 switch(c) { 94 switch(c) {
93 case 'h': 95 case 'h':
94 cerr << PHEADER << endl 96 cerr << PHEADER << endl
95 << PCOPY << endl << endl 97 << PCOPY << endl << endl
96 << " " << argv[0] << " [options] [processes]" << endl << endl << 98 << " " << argv[0] << " [options] [processes]" << endl << endl <<
97#ifdef HAVE_GETOPT_LONG 99#ifdef HAVE_GETOPT_LONG
98 " -h, --help\n" 100 " -h, --help\n"
99 " --usage display this text\n" 101 " --usage display this text\n"
100 " -V, --version display version number\n" 102 " -V, --version display version number\n"
101 " -L, --license show license\n" 103 " -L, --license show license\n"
102 " -f filename, --config=filename\n" 104 " -f filename, --config=filename\n"
103 " specify the configuration file to use\n" 105 " specify the configuration file to use\n"
104 "\n" 106 "\n"
105 " -k, --kill stop running instance (send SIGTERM)\n" 107 " -k, --kill stop running instance (send SIGTERM)\n"
106 " -r, --reload reload running instance (send SIGHUP)\n" 108 " -r, --reload reload running instance (send SIGHUP)\n"
109 " -s signum, --signal=signum\n"
110 " send the specified signal to the running process\n"
107 " -c, --check check if the process is running\n" 111 " -c, --check check if the process is running\n"
108 " (the above commands operate on dudki itself if no\n" 112 " (the above commands operate on dudki itself if no\n"
109 " process name has been specified)\n" 113 " process name has been specified)\n"
110 " -e, --ensure ensure that dudki is running\n" 114 " -e, --ensure ensure that dudki is running\n"
111 " -t, --test test configuration file and exit" 115 " -t, --test test configuration file and exit"
112#else /* !HAVE_GETOPT_LONG */ 116#else /* !HAVE_GETOPT_LONG */
113 " -h display this text\n" 117 " -h display this text\n"
114 " -V display version number\n" 118 " -V display version number\n"
115 " -L show license\n" 119 " -L show license\n"
116 " -f filename specify the configuration file to use\n" 120 " -f filename specify the configuration file to use\n"
117 "\n" 121 "\n"
118 " -k stop running instance (send SIGTERM)\n" 122 " -k stop running instance (send SIGTERM)\n"
119 " -r reload running instance (send SIGHUP)\n" 123 " -r reload running instance (send SIGHUP)\n"
124 " -s signum send the specified signal to the running process\n"
120 " -c check if the process is running\n" 125 " -c check if the process is running\n"
121 " (the above commands operate on dudki itself if no\n" 126 " (the above commands operate on dudki itself if no\n"
122 " process name has been specified)\n" 127 " process name has been specified)\n"
123 " -e ensure that dudki is running\n" 128 " -e ensure that dudki is running\n"
124 " -t test configuration file and exit" 129 " -t test configuration file and exit"
125#endif /* /HAVE_GETOPT_LONG */ 130#endif /* /HAVE_GETOPT_LONG */
126 << endl; 131 << endl;
127 exit(0); 132 exit(0);
128 break; 133 break;
129 case 'V': 134 case 'V':
130 cerr << VERSION << endl; 135 cerr << VERSION << endl;
131 exit(0); 136 exit(0);
132 break; 137 break;
133 case 'L': 138 case 'L':
134 extern const char *COPYING; 139 extern const char *COPYING;
135 cerr << COPYING << endl; 140 cerr << COPYING << endl;
136 exit(0); 141 exit(0);
137 break; 142 break;
138 case 'f': 143 case 'f':
139 config_file = optarg; 144 config_file = optarg;
140 break; 145 break;
141 case 'k': 146 case 'k':
142 if(op!=op_default) { 147 if(op!=op_default) {
143 cerr << "Can't obey two or more orders at once" << endl; 148 cerr << "Can't obey two or more orders at once" << endl;
144 exit(1); 149 exit(1);
145 } 150 }
146 op = op_signal; op_signum = SIGTERM; 151 op = op_signal; op_signum = SIGTERM;
147 break; 152 break;
148 case 'r': 153 case 'r':
149 if(op!=op_default) { 154 if(op!=op_default) {
150 cerr << "Can't obey two or more orders at once" << endl; 155 cerr << "Can't obey two or more orders at once" << endl;
151 exit(1); 156 exit(1);
152 } 157 }
153 op = op_signal; op_signum = SIGHUP; 158 op = op_signal; op_signum = SIGHUP;
154 break; 159 break;
155 case 'c': 160 case 'c':
156 if(op!=op_default) { 161 if(op!=op_default) {
157 cerr << "Can't obey two or more orders at once" << endl; 162 cerr << "Can't obey two or more orders at once" << endl;
158 exit(1); 163 exit(1);
159 } 164 }
160 op = op_signal; op_signum = 0; 165 op = op_signal; op_signum = 0;
161 break; 166 break;
162 case 'e': 167 case 'e':
163 if(op!=op_default) { 168 if(op!=op_default) {
164 cerr << "Can't obey two or more orders at once" << endl; 169 cerr << "Can't obey two or more orders at once" << endl;
165 exit(1); 170 exit(1);
166 } 171 }
167 op = op_ensure; 172 op = op_ensure;
168 break; 173 break;
169 case 't': 174 case 't':
170 if(op!=op_default) { 175 if(op!=op_default) {
171 cerr << "Can't obey two or more orders at once" << endl; 176 cerr << "Can't obey two or more orders at once" << endl;
172 exit(1); 177 exit(1);
173 } 178 }
174 op = op_test; 179 op = op_test;
175 break; 180 break;
181 case 's':
182 if(op!=op_default) {
183 cerr << "Can't obey two or more orders at once" << endl;
184 exit(1);
185 }
186 op = op_signal;
187 errno = 0;
188 op_signum = strtol(optarg,NULL,0);
189 if(errno) {
190 cerr << "Can't obtain the signal value" << endl;
191 exit(1);
192 }
193 break;
176 default: 194 default:
177 cerr << "Huh??" << endl; 195 cerr << "Huh??" << endl;
178 exit(1); 196 exit(1);
179 break; 197 break;
180 } 198 }
181 } 199 }
182 const char *sid = *argv; 200 const char *sid = *argv;
183 const char *t; 201 const char *t;
184 while(t = index(sid,'/')) { 202 while(t = index(sid,'/')) {
185 sid = t; sid++; 203 sid = t; sid++;
186 } 204 }
187 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); 205 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON);
188 configuration config; 206 configuration config;
189 config.parse(config_file); 207 config.parse(config_file);
190 switch(op) { 208 switch(op) {
191 case op_test: 209 case op_test:
192 cerr << "Configuration OK" << endl; 210 cerr << "Configuration OK" << endl;
193 break; 211 break;
194 case op_signal: 212 case op_signal:
195 try { 213 try {
196 if(optind>=argc) { 214 if(optind>=argc) {
197 signal_self(config,op_signum); 215 signal_self(config,op_signum);
198 }else{ 216 }else{
199 int failures = 0; 217 int failures = 0;
200 for(int narg=optind;narg<argc;narg++) { 218 for(int narg=optind;narg<argc;narg++) {
201 try { 219 try {
202 processes_t::const_iterator i = config.processes.find(argv[narg]); 220 processes_t::const_iterator i = config.processes.find(argv[narg]);
203 if(i==config.processes.end()) 221 if(i==config.processes.end())
204 throw runtime_error("no such process configured"); 222 throw runtime_error("no such process configured");
205 i->second.signal(op_signum); 223 i->second.signal(op_signum);
206 }catch(exception& e) { 224 }catch(exception& e) {
207 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl; 225 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl;
208 failures++; 226 failures++;
209 } 227 }
210 } 228 }
211 if(failures) 229 if(failures)
212 throw runtime_error("not all processes have been successfully signaled"); 230 throw runtime_error("not all processes have been successfully signaled");
213 } 231 }
214 if(!op_signum) 232 if(!op_signum)
215 exit(0); 233 exit(0);
216 }catch(exception& e) { 234 }catch(exception& e) {
217 if(!op_signum) 235 if(!op_signum)
218 exit(1); 236 exit(1);
219 } 237 }
220 case op_ensure: 238 case op_ensure:
221 try { 239 try {
222 signal_self(config,0); 240 signal_self(config,0);
223 break; 241 break;
224 }catch(exception& e) { 242 }catch(exception& e) {
225 syslog(LOG_NOTICE,"The dudki process is down, taking its place"); 243 syslog(LOG_NOTICE,"The dudki process is down, taking its place");
226 config.daemonize = true; 244 config.daemonize = true;
227 }catch(int zero) { 245 }catch(int zero) {
228 // we throw zero in case we're ensuring that this very process is running. 246 // we throw zero in case we're ensuring that this very process is running.
229 // we don't have to daemonize if we're daemonic. 247 // we don't have to daemonize if we're daemonic.
230 config.daemonize = false; 248 config.daemonize = false;
231 } 249 }
232 case op_default: 250 case op_default:
233 case op_work: 251 case op_work:
234 { 252 {
235 if(config.daemonize) { 253 if(config.daemonize) {
236 pid_t pf = fork(); 254 pid_t pf = fork();
237 if(pf<0) 255 if(pf<0)
238 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 256 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
239 if(pf) { 257 if(pf) {
240 _exit(0); 258 _exit(0);
241 } 259 }
242 } 260 }
243 pid_file pidfile; 261 pid_file pidfile;
244 pidfile.set(config.pidfile); 262 pidfile.set(config.pidfile);
245 signal(SIGINT,lethal_signal_handler); 263 signal(SIGINT,lethal_signal_handler);
246 signal(SIGABRT,lethal_signal_handler); 264 signal(SIGABRT,lethal_signal_handler);
247 signal(SIGTERM,lethal_signal_handler); 265 signal(SIGTERM,lethal_signal_handler);
248 signal(SIGHUP,sighup_handler); 266 signal(SIGHUP,sighup_handler);
249 sigset_t sset; 267 sigset_t sset;
250 sigemptyset(&sset); 268 sigemptyset(&sset);
251 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT); 269 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT);
252 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP); 270 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP);
253 sigprocmask(SIG_UNBLOCK,&sset,NULL); 271 sigprocmask(SIG_UNBLOCK,&sset,NULL);
254 while(!finishing) { 272 while(!finishing) {
255 check_herd(config); 273 check_herd(config);
256 sleep(config.check_interval); 274 sleep(config.check_interval);
257 } 275 }
258 if(restarting) 276 if(restarting)
259 execvp(_argv[0],_argv); 277 execvp(_argv[0],_argv);
260 } 278 }
261 break; 279 break;
262 default: 280 default:
263 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); 281 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error");
264 } 282 }
265 }catch(exception& e) { 283 }catch(exception& e) {
266 cerr << "Oops: " << e.what() << endl; 284 cerr << "Oops: " << e.what() << endl;
267 return 1; 285 return 1;
268 } 286 }
269} 287}