summaryrefslogtreecommitdiffabout
path: root/src/dudki.cc
Unidiff
Diffstat (limited to 'src/dudki.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--src/dudki.cc61
1 files changed, 39 insertions, 22 deletions
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); 195 try {
192 break; 196 if(optind>=argc) {
193 case op_term: 197 signal_self(config,op_signum);
194 signal_self(config,SIGTERM); 198 }else{
195 break; 199 int failures = 0;
196 case op_check: 200 for(int narg=optind;narg<argc;narg++) {
197 try{ 201 try {
198 signal_self(config,0); 202 processes_t::const_iterator i = config.processes.find(argv[narg]);
199 exit(0); 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)
215 exit(0);
200 }catch(exception& e) { 216 }catch(exception& e) {
201 exit(1); 217 if(!op_signum)
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}