summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--src/dudki.cc2
-rw-r--r--src/process.cc3
2 files changed, 4 insertions, 1 deletions
diff --git a/src/dudki.cc b/src/dudki.cc
index 91a3342..1f95be4 100644
--- a/src/dudki.cc
+++ b/src/dudki.cc
@@ -1,292 +1,294 @@
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 <errno.h>
5#include <iostream> 5#include <iostream>
6#include <fstream> 6#include <fstream>
7#include <stdexcept> 7#include <stdexcept>
8#include <cstring>
9#include <stdlib.h>
8using namespace std; 10using namespace std;
9#include "configuration.h" 11#include "configuration.h"
10#include "util.h" 12#include "util.h"
11 13
12#include "config.h" 14#include "config.h"
13#ifdef HAVE_GETOPT_H 15#ifdef HAVE_GETOPT_H
14# include <getopt.h> 16# include <getopt.h>
15#endif 17#endif
16 18
17#ifndef DEFAULT_CONF_FILE 19#ifndef DEFAULT_CONF_FILE
18# define DEFAULT_CONF_FILE "/etc/dudki.conf" 20# define DEFAULT_CONF_FILE "/etc/dudki.conf"
19#endif 21#endif
20 22
21#define PHEADER PACKAGE " Version " VERSION 23#define PHEADER PACKAGE " Version " VERSION
22#define PCOPY "Copyright (c) 2004-2006 Klever Group" 24#define PCOPY "Copyright (c) 2004-2006 Klever Group"
23 25
24bool finishing = false; 26bool finishing = false;
25bool restarting = false; 27bool restarting = false;
26static char **_argv = NULL; 28static char **_argv = NULL;
27 29
28static void lethal_signal_handler(int signum) { 30static void lethal_signal_handler(int signum) {
29 syslog(LOG_NOTICE,"Lethal signal received. Terminating."); 31 syslog(LOG_NOTICE,"Lethal signal received. Terminating.");
30 finishing = true; 32 finishing = true;
31} 33}
32static void sighup_handler(int signum) { 34static void sighup_handler(int signum) {
33 syslog(LOG_NOTICE,"SUGHUP received, reloading."); 35 syslog(LOG_NOTICE,"SUGHUP received, reloading.");
34 restarting = finishing = true; 36 restarting = finishing = true;
35} 37}
36 38
37void check_herd(configuration& config) { 39void check_herd(configuration& config) {
38 process::prepare_herd(); 40 process::prepare_herd();
39 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) 41 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i)
40 i->second.check(i->first,config); 42 i->second.check(i->first,config);
41 process::unprepare_herd(); 43 process::unprepare_herd();
42} 44}
43 45
44void signal_self(const configuration& config,int signum) { 46void signal_self(const configuration& config,int signum) {
45 ifstream pids(config.pidfile.c_str(),ios::in); 47 ifstream pids(config.pidfile.c_str(),ios::in);
46 if(!pids) 48 if(!pids)
47 throw runtime_error("Can't detect running instance"); 49 throw runtime_error("Can't detect running instance");
48 pid_t pid = 0; 50 pid_t pid = 0;
49 pids >> pid; 51 pids >> pid;
50 if(!pid) 52 if(!pid)
51 throw runtime_error("Can't detect running instance"); 53 throw runtime_error("Can't detect running instance");
52 if(pid==getpid()) 54 if(pid==getpid())
53 throw 0; 55 throw 0;
54 if(kill(pid,signum)) 56 if(kill(pid,signum))
55 throw runtime_error("Failed to signal running instance"); 57 throw runtime_error("Failed to signal running instance");
56} 58}
57 59
58int main(int argc,char **argv) { 60int main(int argc,char **argv) {
59 try { 61 try {
60 _argv = new char*[argc+1]; 62 _argv = new char*[argc+1];
61 if(!_argv) 63 if(!_argv)
62 throw runtime_error("memory allocation problem at the very start"); 64 throw runtime_error("memory allocation problem at the very start");
63 memmove(_argv,argv,sizeof(*_argv)*(argc+1)); 65 memmove(_argv,argv,sizeof(*_argv)*(argc+1));
64 string config_file = DEFAULT_CONF_FILE; 66 string config_file = DEFAULT_CONF_FILE;
65 enum { 67 enum {
66 op_default, 68 op_default,
67 op_work, 69 op_work,
68 op_signal, 70 op_signal,
69 op_ensure, 71 op_ensure,
70 op_test 72 op_test
71 } op = op_default; 73 } op = op_default;
72 int op_signum = 0; 74 int op_signum = 0;
73 while(true) { 75 while(true) {
74 #defineSHORTOPTSTRING "f:hVLrkcets:" 76 #defineSHORTOPTSTRING "f:hVLrkcets:"
75#ifdef HAVE_GETOPT_LONG 77#ifdef HAVE_GETOPT_LONG
76 static struct option opts[] = { 78 static struct option opts[] = {
77 { "help", no_argument, 0, 'h' }, 79 { "help", no_argument, 0, 'h' },
78 { "usage", no_argument, 0, 'h' }, 80 { "usage", no_argument, 0, 'h' },
79 { "version", no_argument, 0, 'V' }, 81 { "version", no_argument, 0, 'V' },
80 { "license", no_argument, 0, 'L' }, 82 { "license", no_argument, 0, 'L' },
81 { "config", required_argument, 0, 'f' }, 83 { "config", required_argument, 0, 'f' },
82 { "kill", no_argument, 0, 'k' }, 84 { "kill", no_argument, 0, 'k' },
83 { "reload", no_argument, 0, 'r' }, 85 { "reload", no_argument, 0, 'r' },
84 { "signal", required_argument, 0, 's' }, 86 { "signal", required_argument, 0, 's' },
85 { "check", no_argument, 0, 'c' }, 87 { "check", no_argument, 0, 'c' },
86 { "ensure", no_argument, 0, 'e' }, 88 { "ensure", no_argument, 0, 'e' },
87 { "test", no_argument, 0, 't' }, 89 { "test", no_argument, 0, 't' },
88 { NULL, 0, 0, 0 } 90 { NULL, 0, 0, 0 }
89 }; 91 };
90 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); 92 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL);
91#else /* !HAVE_GETOPT_LONG */ 93#else /* !HAVE_GETOPT_LONG */
92 int c = getopt(argc,argv,SHORTOPTSTRING); 94 int c = getopt(argc,argv,SHORTOPTSTRING);
93#endif /* /HAVE_GETOPT_LONG */ 95#endif /* /HAVE_GETOPT_LONG */
94 if(c==-1) 96 if(c==-1)
95 break; 97 break;
96 switch(c) { 98 switch(c) {
97 case 'h': 99 case 'h':
98 cerr << PHEADER << endl 100 cerr << PHEADER << endl
99 << PCOPY << endl << endl 101 << PCOPY << endl << endl
100 << " " << argv[0] << " [options] [processes]" << endl << endl << 102 << " " << argv[0] << " [options] [processes]" << endl << endl <<
101#ifdef HAVE_GETOPT_LONG 103#ifdef HAVE_GETOPT_LONG
102 " -h, --help\n" 104 " -h, --help\n"
103 " --usage display this text\n" 105 " --usage display this text\n"
104 " -V, --version display version number\n" 106 " -V, --version display version number\n"
105 " -L, --license show license\n" 107 " -L, --license show license\n"
106 " -f filename, --config=filename\n" 108 " -f filename, --config=filename\n"
107 " specify the configuration file to use\n" 109 " specify the configuration file to use\n"
108 "\n" 110 "\n"
109 " -k, --kill stop running instance (send SIGTERM)\n" 111 " -k, --kill stop running instance (send SIGTERM)\n"
110 " -r, --reload reload running instance (send SIGHUP)\n" 112 " -r, --reload reload running instance (send SIGHUP)\n"
111 " -s signum, --signal=signum\n" 113 " -s signum, --signal=signum\n"
112 " send the specified signal to the running process\n" 114 " send the specified signal to the running process\n"
113 " -c, --check check if the process is running\n" 115 " -c, --check check if the process is running\n"
114 " (the above commands operate on dudki itself if no\n" 116 " (the above commands operate on dudki itself if no\n"
115 " process name has been specified)\n" 117 " process name has been specified)\n"
116 " -e, --ensure ensure that dudki is running\n" 118 " -e, --ensure ensure that dudki is running\n"
117 " -t, --test test configuration file and exit" 119 " -t, --test test configuration file and exit"
118#else /* !HAVE_GETOPT_LONG */ 120#else /* !HAVE_GETOPT_LONG */
119 " -h display this text\n" 121 " -h display this text\n"
120 " -V display version number\n" 122 " -V display version number\n"
121 " -L show license\n" 123 " -L show license\n"
122 " -f filename specify the configuration file to use\n" 124 " -f filename specify the configuration file to use\n"
123 "\n" 125 "\n"
124 " -k stop running instance (send SIGTERM)\n" 126 " -k stop running instance (send SIGTERM)\n"
125 " -r reload running instance (send SIGHUP)\n" 127 " -r reload running instance (send SIGHUP)\n"
126 " -s signum send the specified signal to the running process\n" 128 " -s signum send the specified signal to the running process\n"
127 " -c check if the process is running\n" 129 " -c check if the process is running\n"
128 " (the above commands operate on dudki itself if no\n" 130 " (the above commands operate on dudki itself if no\n"
129 " process name has been specified)\n" 131 " process name has been specified)\n"
130 " -e ensure that dudki is running\n" 132 " -e ensure that dudki is running\n"
131 " -t test configuration file and exit" 133 " -t test configuration file and exit"
132#endif /* /HAVE_GETOPT_LONG */ 134#endif /* /HAVE_GETOPT_LONG */
133 << endl; 135 << endl;
134 exit(0); 136 exit(0);
135 break; 137 break;
136 case 'V': 138 case 'V':
137 cerr << VERSION << endl; 139 cerr << VERSION << endl;
138 exit(0); 140 exit(0);
139 break; 141 break;
140 case 'L': 142 case 'L':
141 extern const char *COPYING; 143 extern const char *COPYING;
142 cerr << COPYING << endl; 144 cerr << COPYING << endl;
143 exit(0); 145 exit(0);
144 break; 146 break;
145 case 'f': 147 case 'f':
146 config_file = optarg; 148 config_file = optarg;
147 break; 149 break;
148 case 'k': 150 case 'k':
149 if(op!=op_default) { 151 if(op!=op_default) {
150 cerr << "Can't obey two or more orders at once" << endl; 152 cerr << "Can't obey two or more orders at once" << endl;
151 exit(1); 153 exit(1);
152 } 154 }
153 op = op_signal; op_signum = SIGTERM; 155 op = op_signal; op_signum = SIGTERM;
154 break; 156 break;
155 case 'r': 157 case 'r':
156 if(op!=op_default) { 158 if(op!=op_default) {
157 cerr << "Can't obey two or more orders at once" << endl; 159 cerr << "Can't obey two or more orders at once" << endl;
158 exit(1); 160 exit(1);
159 } 161 }
160 op = op_signal; op_signum = SIGHUP; 162 op = op_signal; op_signum = SIGHUP;
161 break; 163 break;
162 case 'c': 164 case 'c':
163 if(op!=op_default) { 165 if(op!=op_default) {
164 cerr << "Can't obey two or more orders at once" << endl; 166 cerr << "Can't obey two or more orders at once" << endl;
165 exit(1); 167 exit(1);
166 } 168 }
167 op = op_signal; op_signum = 0; 169 op = op_signal; op_signum = 0;
168 break; 170 break;
169 case 'e': 171 case 'e':
170 if(op!=op_default) { 172 if(op!=op_default) {
171 cerr << "Can't obey two or more orders at once" << endl; 173 cerr << "Can't obey two or more orders at once" << endl;
172 exit(1); 174 exit(1);
173 } 175 }
174 op = op_ensure; 176 op = op_ensure;
175 break; 177 break;
176 case 't': 178 case 't':
177 if(op!=op_default) { 179 if(op!=op_default) {
178 cerr << "Can't obey two or more orders at once" << endl; 180 cerr << "Can't obey two or more orders at once" << endl;
179 exit(1); 181 exit(1);
180 } 182 }
181 op = op_test; 183 op = op_test;
182 break; 184 break;
183 case 's': 185 case 's':
184 if(op!=op_default) { 186 if(op!=op_default) {
185 cerr << "Can't obey two or more orders at once" << endl; 187 cerr << "Can't obey two or more orders at once" << endl;
186 exit(1); 188 exit(1);
187 } 189 }
188 op = op_signal; 190 op = op_signal;
189 errno = 0; 191 errno = 0;
190 op_signum = strtol(optarg,NULL,0); 192 op_signum = strtol(optarg,NULL,0);
191 if(errno) { 193 if(errno) {
192 cerr << "Can't obtain the signal value" << endl; 194 cerr << "Can't obtain the signal value" << endl;
193 exit(1); 195 exit(1);
194 } 196 }
195 break; 197 break;
196 default: 198 default:
197 cerr << "Huh??" << endl; 199 cerr << "Huh??" << endl;
198 exit(1); 200 exit(1);
199 break; 201 break;
200 } 202 }
201 } 203 }
202 const char *sid = *argv; 204 const char *sid = *argv;
203 const char *t; 205 const char *t;
204 while(t = index(sid,'/')) { 206 while(t = index(sid,'/')) {
205 sid = t; sid++; 207 sid = t; sid++;
206 } 208 }
207 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); 209 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON);
208 configuration config; 210 configuration config;
209 config.parse(config_file); 211 config.parse(config_file);
210 switch(op) { 212 switch(op) {
211 case op_test: 213 case op_test:
212 cerr << "Configuration OK" << endl; 214 cerr << "Configuration OK" << endl;
213 break; 215 break;
214 case op_signal: 216 case op_signal:
215 try { 217 try {
216 if(optind>=argc) { 218 if(optind>=argc) {
217 signal_self(config,op_signum); 219 signal_self(config,op_signum);
218 }else{ 220 }else{
219 int failures = 0; 221 int failures = 0;
220 for(int narg=optind;narg<argc;narg++) { 222 for(int narg=optind;narg<argc;narg++) {
221 try { 223 try {
222 processes_t::const_iterator i = config.processes.find(argv[narg]); 224 processes_t::const_iterator i = config.processes.find(argv[narg]);
223 if(i==config.processes.end()) 225 if(i==config.processes.end())
224 throw runtime_error("no such process configured"); 226 throw runtime_error("no such process configured");
225 if(op_signum) 227 if(op_signum)
226 i->second.signal(op_signum); 228 i->second.signal(op_signum);
227 else 229 else
228 i->second.check(); 230 i->second.check();
229 }catch(exception& e) { 231 }catch(exception& e) {
230 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl; 232 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl;
231 failures++; 233 failures++;
232 } 234 }
233 } 235 }
234 if(failures) 236 if(failures)
235 throw runtime_error("not all processes have been successfully signaled"); 237 throw runtime_error("not all processes have been successfully signaled");
236 } 238 }
237 exit(0); 239 exit(0);
238 }catch(exception& e) { 240 }catch(exception& e) {
239 exit(1); 241 exit(1);
240 } 242 }
241 break; 243 break;
242 case op_ensure: 244 case op_ensure:
243 try { 245 try {
244 signal_self(config,0); 246 signal_self(config,0);
245 break; 247 break;
246 }catch(exception& e) { 248 }catch(exception& e) {
247 syslog(LOG_NOTICE,"The dudki process is down, taking its place"); 249 syslog(LOG_NOTICE,"The dudki process is down, taking its place");
248 config.daemonize = true; 250 config.daemonize = true;
249 }catch(int zero) { 251 }catch(int zero) {
250 // we throw zero in case we're ensuring that this very process is running. 252 // we throw zero in case we're ensuring that this very process is running.
251 // we don't have to daemonize if we're daemonic. 253 // we don't have to daemonize if we're daemonic.
252 config.daemonize = false; 254 config.daemonize = false;
253 } 255 }
254 case op_default: 256 case op_default:
255 case op_work: 257 case op_work:
256 { 258 {
257 if(config.daemonize) { 259 if(config.daemonize) {
258 pid_t pf = fork(); 260 pid_t pf = fork();
259 if(pf<0) 261 if(pf<0)
260 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 262 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
261 if(pf) { 263 if(pf) {
262 _exit(0); 264 _exit(0);
263 } 265 }
264 } 266 }
265 pid_file pidfile; 267 pid_file pidfile;
266 pidfile.set(config.pidfile); 268 pidfile.set(config.pidfile);
267 signal(SIGINT,lethal_signal_handler); 269 signal(SIGINT,lethal_signal_handler);
268 signal(SIGABRT,lethal_signal_handler); 270 signal(SIGABRT,lethal_signal_handler);
269 signal(SIGTERM,lethal_signal_handler); 271 signal(SIGTERM,lethal_signal_handler);
270 signal(SIGHUP,sighup_handler); 272 signal(SIGHUP,sighup_handler);
271 sigset_t sset; 273 sigset_t sset;
272 sigemptyset(&sset); 274 sigemptyset(&sset);
273 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT); 275 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT);
274 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP); 276 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP);
275 sigprocmask(SIG_UNBLOCK,&sset,NULL); 277 sigprocmask(SIG_UNBLOCK,&sset,NULL);
276 while(!finishing) { 278 while(!finishing) {
277 check_herd(config); 279 check_herd(config);
278 sleep(config.check_interval); 280 sleep(config.check_interval);
279 } 281 }
280 if(restarting) 282 if(restarting)
281 execvp(_argv[0],_argv); 283 execvp(_argv[0],_argv);
282 } 284 }
283 break; 285 break;
284 default: 286 default:
285 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); 287 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error");
286 } 288 }
287 }catch(exception& e) { 289 }catch(exception& e) {
288 cerr << "Oops: " << e.what() << endl; 290 cerr << "Oops: " << e.what() << endl;
289 return 1; 291 return 1;
290 } 292 }
291 exit(0); 293 exit(0);
292} 294}
diff --git a/src/process.cc b/src/process.cc
index 4807b98..3e9cc2b 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,302 +1,303 @@
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 <dirent.h> 7#include <dirent.h>
8#include <sys/wait.h> 8#include <sys/wait.h>
9#include <syslog.h> 9#include <syslog.h>
10#include <stdlib.h>
10#include <errno.h> 11#include <errno.h>
11#include <iostream> 12#include <iostream>
12#include <fstream> 13#include <fstream>
13#include <sstream> 14#include <sstream>
14#include <stdexcept> 15#include <stdexcept>
15#include <string> 16#include <string>
16#include <vector> 17#include <vector>
17using namespace std; 18using namespace std;
18#include "process.h" 19#include "process.h"
19#include "configuration.h" 20#include "configuration.h"
20 21
21static multimap<string,pid_t> procpids; 22static multimap<string,pid_t> procpids;
22 23
23void process::check() const { 24void process::check() const {
24 if(!pidfile.empty()) { 25 if(!pidfile.empty()) {
25 signal(0); 26 signal(0);
26 }else if(!process_name.empty()) { 27 }else if(!process_name.empty()) {
27 if(procpids.empty()) 28 if(procpids.empty())
28 gather_proc_info(); 29 gather_proc_info();
29 if(procpids.find(process_name)==procpids.end()) 30 if(procpids.find(process_name)==procpids.end())
30 throw runtime_error("no such process"); 31 throw runtime_error("no such process");
31 } // XXX: or else? 32 } // XXX: or else?
32} 33}
33void process::check(const string& id,configuration& config) { 34void process::check(const string& id,configuration& config) {
34 try { 35 try {
35 check(); 36 check();
36 patience = 0; 37 patience = 0;
37 }catch(exception& e) { 38 }catch(exception& e) {
38 if(patience>60) { // TODO: configurable 39 if(patience>60) { // TODO: configurable
39 patience = 0; 40 patience = 0;
40 }else{ 41 }else{
41 if(patience<10) { // TODO: configurable 42 if(patience<10) { // TODO: configurable
42 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str()); 43 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
43 do_notify(id,"Starting up", 44 do_notify(id,"Starting up",
44 "The named process seems to be down. Dudki will try\n" 45 "The named process seems to be down. Dudki will try\n"
45 "to revive it by running the specified command.\n", 46 "to revive it by running the specified command.\n",
46 config); 47 config);
47 try { 48 try {
48 launch(id,config); 49 launch(id,config);
49 }catch(exception& e) { 50 }catch(exception& e) {
50 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 51 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
51 } 52 }
52 }else if(patience==10){ // TODO: configurable like the above 53 }else if(patience==10){ // TODO: configurable like the above
53 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str()); 54 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
54 do_notify(id,"Giving up", 55 do_notify(id,"Giving up",
55 "After a number of attempts to relaunch the named process\n" 56 "After a number of attempts to relaunch the named process\n"
56 "It still seems to be down. Dudki is giving up attempts\n" 57 "It still seems to be down. Dudki is giving up attempts\n"
57 "to revive the process for a while.\n", 58 "to revive the process for a while.\n",
58 config); 59 config);
59 } 60 }
60 patience++; 61 patience++;
61 } 62 }
62 } 63 }
63} 64}
64 65
65void process::launch(const string& id,configuration& config) { 66void process::launch(const string& id,configuration& config) {
66 uid_t uid = (uid_t)-1; 67 uid_t uid = (uid_t)-1;
67 gid_t gid = (gid_t)-1; 68 gid_t gid = (gid_t)-1;
68 if(!user.empty()) { 69 if(!user.empty()) {
69 struct passwd *ptmp = getpwnam(user.c_str()); 70 struct passwd *ptmp = getpwnam(user.c_str());
70 if(ptmp) { 71 if(ptmp) {
71 uid = ptmp->pw_uid; 72 uid = ptmp->pw_uid;
72 gid = ptmp->pw_gid; 73 gid = ptmp->pw_gid;
73 }else{ 74 }else{
74 errno=0; 75 errno=0;
75 uid = strtol(user.c_str(),NULL,0); 76 uid = strtol(user.c_str(),NULL,0);
76 if(errno) 77 if(errno)
77 throw runtime_error("Failed to resolve User value to uid"); 78 throw runtime_error("Failed to resolve User value to uid");
78 } 79 }
79 } 80 }
80 if(!group.empty()) { 81 if(!group.empty()) {
81 struct group *gtmp = getgrnam(group.c_str()); 82 struct group *gtmp = getgrnam(group.c_str());
82 if(gtmp) { 83 if(gtmp) {
83 gid = gtmp->gr_gid; 84 gid = gtmp->gr_gid;
84 }else{ 85 }else{
85 errno = 0; 86 errno = 0;
86 gid = strtol(group.c_str(),NULL,0); 87 gid = strtol(group.c_str(),NULL,0);
87 if(errno) 88 if(errno)
88 throw runtime_error("Failed to reslove Group value to gid"); 89 throw runtime_error("Failed to reslove Group value to gid");
89 } 90 }
90 } 91 }
91 pid_t p = fork(); 92 pid_t p = fork();
92 if(p<0) 93 if(p<0)
93 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 94 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
94 if(!p) { 95 if(!p) {
95 // child 96 // child
96 try { 97 try {
97 setsid(); 98 setsid();
98 if(!group.empty()) { 99 if(!group.empty()) {
99 if(user.empty()) { 100 if(user.empty()) {
100 if((getgid()!=gid) && setgid(gid)) 101 if((getgid()!=gid) && setgid(gid))
101 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); 102 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
102 }else{ 103 }else{
103 if(initgroups(user.c_str(),gid)) 104 if(initgroups(user.c_str(),gid))
104 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()"); 105 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()");
105 } 106 }
106 } 107 }
107 if(!chroot.empty()) { 108 if(!chroot.empty()) {
108 if(::chroot(chroot.c_str())) 109 if(::chroot(chroot.c_str()))
109 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); 110 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
110 } 111 }
111 if(!user.empty()) { 112 if(!user.empty()) {
112 if((getuid()!=uid) && setuid(uid)) 113 if((getuid()!=uid) && setuid(uid))
113 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); 114 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
114 } 115 }
115 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; 116 char *argv[] = { const_cast<char*>("/bin/sh"), const_cast<char*>("-c"), (char*)restart_cmd.c_str(), NULL };
116 close(0); close(1); close(2); 117 close(0); close(1); close(2);
117 execv("/bin/sh",argv); 118 execv("/bin/sh",argv);
118 }catch(exception& e) { 119 }catch(exception& e) {
119 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 120 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
120 } 121 }
121 _exit(-1); 122 _exit(-1);
122 } 123 }
123 // parent 124 // parent
124 int rv; 125 int rv;
125 if(waitpid(p,&rv,0)<0) 126 if(waitpid(p,&rv,0)<0)
126 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); 127 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
127} 128}
128 129
129void process::do_notify(const string& id,const string& event,const string& description,configuration& config) { 130void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
130 string the_notify; 131 string the_notify;
131 if(!notify.empty()) 132 if(!notify.empty())
132 the_notify=notify; 133 the_notify=notify;
133 else if(!config.notify.empty()) 134 else if(!config.notify.empty())
134 the_notify=config.notify; 135 the_notify=config.notify;
135 else 136 else
136 return; 137 return;
137 try { 138 try {
138 string::size_type colon = the_notify.find(':'); 139 string::size_type colon = the_notify.find(':');
139 if(colon==string::npos) 140 if(colon==string::npos)
140 throw runtime_error("invalid notify action specification"); 141 throw runtime_error("invalid notify action specification");
141 string nschema = the_notify.substr(0,colon); 142 string nschema = the_notify.substr(0,colon);
142 string ntarget = the_notify.substr(colon+1); 143 string ntarget = the_notify.substr(colon+1);
143 if(nschema=="mailto") { 144 if(nschema=="mailto") {
144 notify_mailto(ntarget,id,event,description,config); 145 notify_mailto(ntarget,id,event,description,config);
145 }else 146 }else
146 throw runtime_error("unrecognized notification schema"); 147 throw runtime_error("unrecognized notification schema");
147 }catch(exception& e) { 148 }catch(exception& e) {
148 syslog(LOG_ERR,"Notification error: %s",e.what()); 149 syslog(LOG_ERR,"Notification error: %s",e.what());
149 } 150 }
150} 151}
151 152
152void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) { 153void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
153 int files[2]; 154 int files[2];
154 if(pipe(files)) 155 if(pipe(files))
155 throw runtime_error("Failed to pipe()"); 156 throw runtime_error("Failed to pipe()");
156 pid_t pid = vfork(); 157 pid_t pid = vfork();
157 if(pid==-1) { 158 if(pid==-1) {
158 close(files[0]); 159 close(files[0]);
159 close(files[1]); 160 close(files[1]);
160 throw runtime_error("Failed to vfork()"); 161 throw runtime_error("Failed to vfork()");
161 } 162 }
162 if(!pid) { 163 if(!pid) {
163 // child 164 // child
164 if(dup2(files[0],0)!=0) 165 if(dup2(files[0],0)!=0)
165 _exit(-1); 166 _exit(-1);
166 close(1); 167 close(1);
167 close(files[0]); 168 close(files[0]);
168 close(files[1]); 169 close(files[1]);
169 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL); 170 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
170 _exit(-1); 171 _exit(-1);
171 } 172 }
172 // parent 173 // parent
173 int status; 174 int status;
174 if(waitpid(pid,&status,WNOHANG)) { 175 if(waitpid(pid,&status,WNOHANG)) {
175 close(files[0]); 176 close(files[0]);
176 close(files[1]); 177 close(files[1]);
177 throw runtime_error("vfork()ed sendmail child exited unexpectedly"); 178 throw runtime_error("vfork()ed sendmail child exited unexpectedly");
178 } 179 }
179 close(files[0]); 180 close(files[0]);
180 FILE *mta = fdopen(files[1],"w"); 181 FILE *mta = fdopen(files[1],"w");
181 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { 182 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
182 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 183 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
183 } 184 }
184 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { 185 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
185 if(mailto_headers.find(i->first)!=mailto_headers.end()) 186 if(mailto_headers.find(i->first)!=mailto_headers.end())
186 continue; 187 continue;
187 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 188 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
188 } 189 }
189 fprintf(mta, 190 fprintf(mta,
190 "Subject: [%s] %s\n\n" 191 "Subject: [%s] %s\n\n"
191 "%s\n" 192 "%s\n"
192 "---\n" 193 "---\n"
193 "This message was sent automatically by the 'dudki' daemon\n", 194 "This message was sent automatically by the 'dudki' daemon\n",
194 id.c_str(), event.c_str(), 195 id.c_str(), event.c_str(),
195 description.c_str() ); 196 description.c_str() );
196 fclose(mta); 197 fclose(mta);
197 waitpid(pid,&status,0); 198 waitpid(pid,&status,0);
198 // TODO: check the return code 199 // TODO: check the return code
199} 200}
200 201
201void process::signal(int signum) const { 202void process::signal(int signum) const {
202 if(!pidfile.empty()) { 203 if(!pidfile.empty()) {
203 ifstream pids(pidfile.c_str(),ios::in); 204 ifstream pids(pidfile.c_str(),ios::in);
204 if(!pids) 205 if(!pids)
205 throw runtime_error("no pidfile found"); 206 throw runtime_error("no pidfile found");
206 pid_t pid = 0; 207 pid_t pid = 0;
207 pids >> pid; 208 pids >> pid;
208 pids.close(); 209 pids.close();
209 if(!pid) 210 if(!pid)
210 throw runtime_error("no pid in pidfile"); 211 throw runtime_error("no pid in pidfile");
211 if(kill(pid,signum)) 212 if(kill(pid,signum))
212 throw runtime_error("failed to signal process"); 213 throw runtime_error("failed to signal process");
213 }else if(!process_name.empty()) { 214 }else if(!process_name.empty()) {
214 if(procpids.empty()) 215 if(procpids.empty())
215 gather_proc_info(); 216 gather_proc_info();
216 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name); 217 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name);
217 int count = 0; 218 int count = 0;
218 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) { 219 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) {
219 pid_t pid = i->second; 220 pid_t pid = i->second;
220 if(kill(i->second,signum)) 221 if(kill(i->second,signum))
221 throw runtime_error("failed to signal process"); 222 throw runtime_error("failed to signal process");
222 count++; 223 count++;
223 } 224 }
224 if(!count) 225 if(!count)
225 throw runtime_error("no running instance detected"); 226 throw runtime_error("no running instance detected");
226 }else 227 }else
227 throw runtime_error("nothing is known about the process"); 228 throw runtime_error("nothing is known about the process");
228} 229}
229 230
230void process::prepare_herd() { 231void process::prepare_herd() {
231 procpids.clear(); 232 procpids.clear();
232} 233}
233void process::unprepare_herd() { 234void process::unprepare_herd() {
234 procpids.clear(); 235 procpids.clear();
235} 236}
236void process::gather_proc_info() { 237void process::gather_proc_info() {
237 vector<pid_t> allpids; 238 vector<pid_t> allpids;
238 DIR *pd = opendir("/proc"); 239 DIR *pd = opendir("/proc");
239 if(!pd) 240 if(!pd)
240 throw runtime_error("failed to open /proc"); 241 throw runtime_error("failed to open /proc");
241 struct dirent *pde; 242 struct dirent *pde;
242 pid_t selfpid = getpid(); 243 pid_t selfpid = getpid();
243 while(pde=readdir(pd)) { 244 while(pde=readdir(pd)) {
244 errno=0; 245 errno=0;
245 pid_t pid = atoi(pde->d_name); 246 pid_t pid = atoi(pde->d_name);
246 if((!pid) || pid==selfpid) 247 if((!pid) || pid==selfpid)
247 continue; 248 continue;
248 allpids.push_back(pid); 249 allpids.push_back(pid);
249 } 250 }
250 closedir(pd); 251 closedir(pd);
251 char s[256]; 252 char s[256];
252 procpids.clear(); 253 procpids.clear();
253 for(vector<pid_t>::const_iterator i=allpids.begin();i!=allpids.end();++i) { 254 for(vector<pid_t>::const_iterator i=allpids.begin();i!=allpids.end();++i) {
254 int r = snprintf(s,sizeof(s),"/proc/%d/stat",*i); 255 int r = snprintf(s,sizeof(s),"/proc/%d/stat",*i);
255 if(r>=sizeof(s) || r<1) 256 if(r>=sizeof(s) || r<1)
256 continue; 257 continue;
257 string cmd; 258 string cmd;
258 ifstream ss(s,ios::in); 259 ifstream ss(s,ios::in);
259 if(ss) { 260 if(ss) {
260 getline(ss,cmd); 261 getline(ss,cmd);
261 string::size_type op = cmd.find('('); 262 string::size_type op = cmd.find('(');
262 if(op==string::npos) 263 if(op==string::npos)
263 continue; 264 continue;
264 cmd.erase(0,op+1); 265 cmd.erase(0,op+1);
265 string::size_type cp = cmd.find(')'); 266 string::size_type cp = cmd.find(')');
266 if(cp==string::npos) 267 if(cp==string::npos)
267 continue; 268 continue;
268 cmd.erase(cp); 269 cmd.erase(cp);
269 }else{ 270 }else{
270 r = snprintf(s,sizeof(s),"/proc/%d/status",*i); 271 r = snprintf(s,sizeof(s),"/proc/%d/status",*i);
271 if(r>=sizeof(s) || r<1) 272 if(r>=sizeof(s) || r<1)
272 continue; 273 continue;
273 ifstream ss(s,ios::in); 274 ifstream ss(s,ios::in);
274 if(!ss) 275 if(!ss)
275 continue; 276 continue;
276 ss >> cmd; 277 ss >> cmd;
277 if(cmd.empty()) 278 if(cmd.empty())
278 continue; 279 continue;
279 } 280 }
280 r = snprintf(s,sizeof(s),"/proc/%d/cmdline",*i); 281 r = snprintf(s,sizeof(s),"/proc/%d/cmdline",*i);
281 if(r>=sizeof(s) || r<1) 282 if(r>=sizeof(s) || r<1)
282 continue; 283 continue;
283 ifstream cs(s,ios::binary); 284 ifstream cs(s,ios::binary);
284 if(!cs) 285 if(!cs)
285 continue; 286 continue;
286 string command; 287 string command;
287 while(cs) { 288 while(cs) {
288 string cl; 289 string cl;
289 getline(cs,cl,(char)0); 290 getline(cs,cl,(char)0);
290 string::size_type lsl = cl.rfind('/'); 291 string::size_type lsl = cl.rfind('/');
291 if(lsl!=string::npos) 292 if(lsl!=string::npos)
292 cl.erase(0,lsl+1); 293 cl.erase(0,lsl+1);
293 if(cl.substr(0,cmd.length())==cmd) { 294 if(cl.substr(0,cmd.length())==cmd) {
294 command = cl; 295 command = cl;
295 break; 296 break;
296 } 297 }
297 } 298 }
298 procpids.insert(pair<string,pid_t>(cmd,*i)); 299 procpids.insert(pair<string,pid_t>(cmd,*i));
299 if((!command.empty()) && cmd!=command) 300 if((!command.empty()) && cmd!=command)
300 procpids.insert(pair<string,pid_t>(command,*i)); 301 procpids.insert(pair<string,pid_t>(command,*i));
301 } 302 }
302} 303}