summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--NEWS.xml3
-rw-r--r--man/dudki.conf.5.in6
-rw-r--r--src/configuration.cc5
-rw-r--r--src/dudki.cc5
-rw-r--r--src/process.cc98
-rw-r--r--src/process.h6
6 files changed, 120 insertions, 3 deletions
diff --git a/NEWS.xml b/NEWS.xml
index cd12d05..cc6085d 100644
--- a/NEWS.xml
+++ b/NEWS.xml
@@ -1,14 +1,15 @@
1<?xml version="1.0" encoding="us-ascii"?> 1<?xml version="1.0" encoding="us-ascii"?>
2<news> 2<news>
3 <version version="0.2" date="July 23rd, 2004"> 3 <version version="0.2" date="July 24th, 2004">
4 <ni>now dudki sends arbitrary signals to the processes being monitored from the command line</ni> 4 <ni>now dudki sends arbitrary signals to the processes being monitored from the command line</ni>
5 <ni>detection of running processes which do not keep pidfiles, using process name</ni>
5 </version> 6 </version>
6 <version version="0.1" date="July 21st, 2004"> 7 <version version="0.1" date="July 21st, 2004">
7 <ni><kbd>initgroups()</kbd> called before executing <kbd>RestartCommand</kbd></ni> 8 <ni><kbd>initgroups()</kbd> called before executing <kbd>RestartCommand</kbd></ni>
8 <ni>more civilized way of restarting on <kbd>SIGHUP</kbd></ni> 9 <ni>more civilized way of restarting on <kbd>SIGHUP</kbd></ni>
9 <ni>minor changes to build process</ni> 10 <ni>minor changes to build process</ni>
10 </version> 11 </version>
11 <version version="0.0" date="July 11th, 2004"> 12 <version version="0.0" date="July 11th, 2004">
12 <ni>Initial release</ni> 13 <ni>Initial release</ni>
13 </version> 14 </version>
14</news> 15</news>
diff --git a/man/dudki.conf.5.in b/man/dudki.conf.5.in
index 23f636d..7d365d3 100644
--- a/man/dudki.conf.5.in
+++ b/man/dudki.conf.5.in
@@ -1,25 +1,25 @@
1.TH dudki.conf 5 "July 9th, 2004" "dudki.conf(5)" "Klever Group (http://www.klever.net/)" 1.TH dudki.conf 5 "July 24th, 2004" "dudki.conf(5)" "Klever Group (http://www.klever.net/)"
2.hla en 2.hla en
3 3
4.SH NAME 4.SH NAME
5 5
6dudki.conf \- The configuration file for the dudki process 6dudki.conf \- The configuration file for the dudki process
7monitoring daemon 7monitoring daemon
8 8
9.SH SYNOPSIS 9.SH SYNOPSIS
10 10
11The dudki.conf file is a runtime configuration file for the dudki 11The dudki.conf file is a runtime configuration file for the dudki
12process monitoring daemon. It contains the information about the process 12process monitoring daemon. It contains the information about the process
13being monitored and the instruction on how to cope with the processes. 13being monitored and the instruction on how to cope with the processes.
14 14
15.SH FILE FORMAT 15.SH FILE FORMAT
16 16
17The file consist of the global configuration and per-process 17The file consist of the global configuration and per-process
18configuration sections. The global configuration controls general 18configuration sections. The global configuration controls general
19dudki behaviour and sets defaults for per-process configuration 19dudki behaviour and sets defaults for per-process configuration
20directives. 20directives.
21 21
22.SH GLOBAL CONFIGURATION 22.SH GLOBAL CONFIGURATION
23 23
24.TP 24.TP
25\fBCheckInterval\fR \fIseconds\fR 25\fBCheckInterval\fR \fIseconds\fR
@@ -39,48 +39,52 @@ Specifies where to store dudki's process id (default is
39Sets global defaults for process section's MailtoHeader directive. See 39Sets global defaults for process section's MailtoHeader directive. See
40below. 40below.
41.TP 41.TP
42\fBNotify\fR \fIschema\fR:\fItarget\fR 42\fBNotify\fR \fIschema\fR:\fItarget\fR
43Sets global default for per process notification. See below. 43Sets global default for per process notification. See below.
44 44
45.TP 45.TP
46\fB<Process\fR \fIidentifier\fR\fB>\fR 46\fB<Process\fR \fIidentifier\fR\fB>\fR
47Starts per process configuration section. The process will be referenced 47Starts per process configuration section. The process will be referenced
48using the short descriptive name specified (for example in email 48using the short descriptive name specified (for example in email
49notifications). 49notifications).
50 50
51.SH PER-PROCESS CONFIGURATION 51.SH PER-PROCESS CONFIGURATION
52 52
53Per-process configuration sections specifies the processes to monitor. 53Per-process configuration sections specifies the processes to monitor.
54And parameters pertaining to the process. 54And parameters pertaining to the process.
55 55
56.TP 56.TP
57\fBPidFile\fR \fIfilename\fR 57\fBPidFile\fR \fIfilename\fR
58Specifies the file where to fetch process id of the process being 58Specifies the file where to fetch process id of the process being
59monitored from. The absence of file, as well as the absence of process 59monitored from. The absence of file, as well as the absence of process
60specified by the pid stored in the file signifies the process death and 60specified by the pid stored in the file signifies the process death and
61triggers restart. 61triggers restart.
62.TP 62.TP
63\fBProcessName\fR \fIprocess name\fR
64Specifies the name of the process. The alternative way to find process if it
65doesn't keep pid in the file. Similar to \fBpidof\fR(1).
66.TP
63\fBRestartCommand\fR \fIcommand\fR 67\fBRestartCommand\fR \fIcommand\fR
64Specifies the command to run in order to restart the process. 68Specifies the command to run in order to restart the process.
65.TP 69.TP
66\fBUser\fR \fIuser\fR 70\fBUser\fR \fIuser\fR
67Specifies the unix user to change to before executing the command 71Specifies the unix user to change to before executing the command
68specified by \fBRestartCommand\fR. 72specified by \fBRestartCommand\fR.
69.TP 73.TP
70\fBGroup\fR \fIgroup\fR 74\fBGroup\fR \fIgroup\fR
71Specifies the unix group to change to before executing the command 75Specifies the unix group to change to before executing the command
72specified by \fBRestartCommand\fR. 76specified by \fBRestartCommand\fR.
73.TP 77.TP
74\fBChroot\fR \fIpath\fR 78\fBChroot\fR \fIpath\fR
75Specifies the directory to set filesystem root to before executing the 79Specifies the directory to set filesystem root to before executing the
76command specified by by \fBRestartCommand\fR. 80command specified by by \fBRestartCommand\fR.
77.TP 81.TP
78\fBMailtoHeader\fR \fIheader\fR \fIcontent\fR 82\fBMailtoHeader\fR \fIheader\fR \fIcontent\fR
79Specifies extra headers to add to mailto: notifications sent by the 83Specifies extra headers to add to mailto: notifications sent by the
80dudki daemon. Headers specified in per-process section override the 84dudki daemon. Headers specified in per-process section override the
81ones specified globally.A 85ones specified globally.A
82.TP 86.TP
83\fBNotify\fR \fIschema\fR:\fItarget\fR 87\fBNotify\fR \fIschema\fR:\fItarget\fR
84Specifies the contact to be notified whenever something notable happens 88Specifies the contact to be notified whenever something notable happens
85to the process. The only schema currently supported is 'mailto:'. 89to the process. The only schema currently supported is 'mailto:'.
86.TP 90.TP
diff --git a/src/configuration.cc b/src/configuration.cc
index eb010c1..edc8c04 100644
--- a/src/configuration.cc
+++ b/src/configuration.cc
@@ -71,72 +71,77 @@ static DOTCONF_CB(dco_notify) { dc_context *dcc = (dc_context*)ctx;
71 break; 71 break;
72 case DCC_PROCESS: 72 case DCC_PROCESS:
73 dcc->ps->notify = cmd->data.str; 73 dcc->ps->notify = cmd->data.str;
74 break; 74 break;
75 default: 75 default:
76 return "Unexpected Notify"; 76 return "Unexpected Notify";
77 } 77 }
78 return NULL; 78 return NULL;
79} 79}
80 80
81static DOTCONF_CB(dco_process) { dc_context *dcc = (dc_context*)ctx; 81static DOTCONF_CB(dco_process) { dc_context *dcc = (dc_context*)ctx;
82 string id = cmd->data.str; 82 string id = cmd->data.str;
83 if(id[id.length()-1]=='>') 83 if(id[id.length()-1]=='>')
84 id.erase(id.length()-1); 84 id.erase(id.length()-1);
85 dcc->ps = &(dcc->cf->processes[id]); 85 dcc->ps = &(dcc->cf->processes[id]);
86 dcc->ctx = DCC_PROCESS; 86 dcc->ctx = DCC_PROCESS;
87 return NULL; 87 return NULL;
88} 88}
89static DOTCONF_CB(dco__process) { dc_context *dcc = (dc_context*)ctx; 89static DOTCONF_CB(dco__process) { dc_context *dcc = (dc_context*)ctx;
90 dcc->ps = NULL; 90 dcc->ps = NULL;
91 dcc->ctx = DCC_ROOT; 91 dcc->ctx = DCC_ROOT;
92 return NULL; 92 return NULL;
93} 93}
94 94
95static DOTCONF_CB(dco_process_name) { dc_context *dcc = (dc_context*)ctx;
96 dcc->ps->process_name = cmd->data.str;
97 return NULL;
98}
95static DOTCONF_CB(dco_restart_command) { dc_context *dcc = (dc_context*)ctx; 99static DOTCONF_CB(dco_restart_command) { dc_context *dcc = (dc_context*)ctx;
96 dcc->ps->restart_cmd = cmd->data.str; 100 dcc->ps->restart_cmd = cmd->data.str;
97 return NULL; 101 return NULL;
98} 102}
99static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx; 103static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx;
100 dcc->ps->user = cmd->data.str; 104 dcc->ps->user = cmd->data.str;
101 return NULL; 105 return NULL;
102} 106}
103static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx; 107static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx;
104 dcc->ps->group = cmd->data.str; 108 dcc->ps->group = cmd->data.str;
105 return NULL; 109 return NULL;
106} 110}
107static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx; 111static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx;
108 dcc->ps->chroot = cmd->data.str; 112 dcc->ps->chroot = cmd->data.str;
109 return NULL; 113 return NULL;
110} 114}
111 115
112static const configoption_t dc_options[] = { 116static const configoption_t dc_options[] = {
113 { "CheckInterval", ARG_INT, dco_check_interval, NULL, DCC_ROOT }, 117 { "CheckInterval", ARG_INT, dco_check_interval, NULL, DCC_ROOT },
114 { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT }, 118 { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT },
115 { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT|DCC_PROCESS }, 119 { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT|DCC_PROCESS },
116 { "MailtoHeader", ARG_STR, dco_mailto_header, NULL, DCC_ROOT|DCC_PROCESS }, 120 { "MailtoHeader", ARG_STR, dco_mailto_header, NULL, DCC_ROOT|DCC_PROCESS },
117 { "Notify", ARG_STR, dco_notify, NULL, DCC_ROOT|DCC_PROCESS }, 121 { "Notify", ARG_STR, dco_notify, NULL, DCC_ROOT|DCC_PROCESS },
118 { "<Process", ARG_STR, dco_process, NULL, DCC_ROOT }, 122 { "<Process", ARG_STR, dco_process, NULL, DCC_ROOT },
123 { "ProcessName", ARG_STR, dco_process_name, NULL, DCC_PROCESS },
119 { "RestartCommand", ARG_STR, dco_restart_command, NULL, DCC_PROCESS }, 124 { "RestartCommand", ARG_STR, dco_restart_command, NULL, DCC_PROCESS },
120 { "User", ARG_STR, dco_user, NULL, DCC_PROCESS }, 125 { "User", ARG_STR, dco_user, NULL, DCC_PROCESS },
121 { "Group", ARG_STR, dco_group, NULL, DCC_PROCESS }, 126 { "Group", ARG_STR, dco_group, NULL, DCC_PROCESS },
122 { "Chroot", ARG_STR, dco_chroot, NULL, DCC_PROCESS }, 127 { "Chroot", ARG_STR, dco_chroot, NULL, DCC_PROCESS },
123 { "</Process>", ARG_NONE, dco__process, NULL, DCC_PROCESS }, 128 { "</Process>", ARG_NONE, dco__process, NULL, DCC_PROCESS },
124 LAST_OPTION 129 LAST_OPTION
125}; 130};
126 131
127static const char *dc_context_checker(command_t *cmd,unsigned long mask) { 132static const char *dc_context_checker(command_t *cmd,unsigned long mask) {
128 dc_context *dcc = (dc_context*)cmd->context; 133 dc_context *dcc = (dc_context*)cmd->context;
129 if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) ) 134 if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) )
130 return NULL; 135 return NULL;
131 return "misplaced option"; 136 return "misplaced option";
132} 137}
133static FUNC_ERRORHANDLER(dc_error_handler) { 138static FUNC_ERRORHANDLER(dc_error_handler) {
134 throw runtime_error(string("error parsing config file: ")+msg); 139 throw runtime_error(string("error parsing config file: ")+msg);
135} 140}
136 141
137void configuration::parse(const string& cfile) { 142void configuration::parse(const string& cfile) {
138 struct dc_context dcc; 143 struct dc_context dcc;
139 dcc.cf = this; 144 dcc.cf = this;
140 dcc.ctx = DCC_ROOT; 145 dcc.ctx = DCC_ROOT;
141 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE); 146 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
142 if(!cf) 147 if(!cf)
diff --git a/src/dudki.cc b/src/dudki.cc
index 9562079..c966695 100644
--- a/src/dudki.cc
+++ b/src/dudki.cc
@@ -14,50 +14,52 @@ using namespace std;
14# include <getopt.h> 14# include <getopt.h>
15#endif 15#endif
16 16
17#ifndef DEFAULT_CONF_FILE 17#ifndef DEFAULT_CONF_FILE
18# define DEFAULT_CONF_FILE "/etc/dudki.conf" 18# define DEFAULT_CONF_FILE "/etc/dudki.conf"
19#endif 19#endif
20 20
21#define PHEADER PACKAGE " Version " VERSION 21#define PHEADER PACKAGE " Version " VERSION
22#define PCOPY "Copyright (c) 2004 Klever Group" 22#define PCOPY "Copyright (c) 2004 Klever Group"
23 23
24bool finishing = false; 24bool finishing = false;
25bool restarting = false; 25bool restarting = false;
26static char **_argv = NULL; 26static char **_argv = NULL;
27 27
28static void lethal_signal_handler(int signum) { 28static void lethal_signal_handler(int signum) {
29 syslog(LOG_NOTICE,"Lethal signal received. Terminating."); 29 syslog(LOG_NOTICE,"Lethal signal received. Terminating.");
30 finishing = true; 30 finishing = true;
31} 31}
32static void sighup_handler(int signum) { 32static void sighup_handler(int signum) {
33 syslog(LOG_NOTICE,"SUGHUP received, reloading."); 33 syslog(LOG_NOTICE,"SUGHUP received, reloading.");
34 restarting = finishing = true; 34 restarting = finishing = true;
35} 35}
36 36
37void check_herd(configuration& config) { 37void check_herd(configuration& config) {
38 process::prepare_herd();
38 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) 39 for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i)
39 i->second.check(i->first,config); 40 i->second.check(i->first,config);
41 process::unprepare_herd();
40} 42}
41 43
42void signal_self(const configuration& config,int signum) { 44void signal_self(const configuration& config,int signum) {
43 ifstream pids(config.pidfile.c_str(),ios::in); 45 ifstream pids(config.pidfile.c_str(),ios::in);
44 if(!pids) 46 if(!pids)
45 throw runtime_error("Can't detect running instance"); 47 throw runtime_error("Can't detect running instance");
46 pid_t pid = 0; 48 pid_t pid = 0;
47 pids >> pid; 49 pids >> pid;
48 if(!pid) 50 if(!pid)
49 throw runtime_error("Can't detect running instance"); 51 throw runtime_error("Can't detect running instance");
50 if(pid==getpid()) 52 if(pid==getpid())
51 throw 0; 53 throw 0;
52 if(kill(pid,signum)) 54 if(kill(pid,signum))
53 throw runtime_error("Failed to signal running instance"); 55 throw runtime_error("Failed to signal running instance");
54} 56}
55 57
56int main(int argc,char **argv) { 58int main(int argc,char **argv) {
57 try { 59 try {
58 _argv = new char*[argc+1]; 60 _argv = new char*[argc+1];
59 if(!_argv) 61 if(!_argv)
60 throw runtime_error("memory allocation problem at the very start"); 62 throw runtime_error("memory allocation problem at the very start");
61 memmove(_argv,argv,sizeof(*_argv)*(argc+1)); 63 memmove(_argv,argv,sizeof(*_argv)*(argc+1));
62 string config_file = DEFAULT_CONF_FILE; 64 string config_file = DEFAULT_CONF_FILE;
63 enum { 65 enum {
@@ -199,49 +201,52 @@ int main(int argc,char **argv) {
199 } 201 }
200 const char *sid = *argv; 202 const char *sid = *argv;
201 const char *t; 203 const char *t;
202 while(t = index(sid,'/')) { 204 while(t = index(sid,'/')) {
203 sid = t; sid++; 205 sid = t; sid++;
204 } 206 }
205 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); 207 openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON);
206 configuration config; 208 configuration config;
207 config.parse(config_file); 209 config.parse(config_file);
208 switch(op) { 210 switch(op) {
209 case op_test: 211 case op_test:
210 cerr << "Configuration OK" << endl; 212 cerr << "Configuration OK" << endl;
211 break; 213 break;
212 case op_signal: 214 case op_signal:
213 try { 215 try {
214 if(optind>=argc) { 216 if(optind>=argc) {
215 signal_self(config,op_signum); 217 signal_self(config,op_signum);
216 }else{ 218 }else{
217 int failures = 0; 219 int failures = 0;
218 for(int narg=optind;narg<argc;narg++) { 220 for(int narg=optind;narg<argc;narg++) {
219 try { 221 try {
220 processes_t::const_iterator i = config.processes.find(argv[narg]); 222 processes_t::const_iterator i = config.processes.find(argv[narg]);
221 if(i==config.processes.end()) 223 if(i==config.processes.end())
222 throw runtime_error("no such process configured"); 224 throw runtime_error("no such process configured");
225 if(op_signum)
223 i->second.signal(op_signum); 226 i->second.signal(op_signum);
227 else
228 i->second.check();
224 }catch(exception& e) { 229 }catch(exception& e) {
225 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl; 230 cerr << "dudki(" << argv[narg] << "): " << e.what() << endl;
226 failures++; 231 failures++;
227 } 232 }
228 } 233 }
229 if(failures) 234 if(failures)
230 throw runtime_error("not all processes have been successfully signaled"); 235 throw runtime_error("not all processes have been successfully signaled");
231 } 236 }
232 if(!op_signum) 237 if(!op_signum)
233 exit(0); 238 exit(0);
234 }catch(exception& e) { 239 }catch(exception& e) {
235 if(!op_signum) 240 if(!op_signum)
236 exit(1); 241 exit(1);
237 } 242 }
238 case op_ensure: 243 case op_ensure:
239 try { 244 try {
240 signal_self(config,0); 245 signal_self(config,0);
241 break; 246 break;
242 }catch(exception& e) { 247 }catch(exception& e) {
243 syslog(LOG_NOTICE,"The dudki process is down, taking its place"); 248 syslog(LOG_NOTICE,"The dudki process is down, taking its place");
244 config.daemonize = true; 249 config.daemonize = true;
245 }catch(int zero) { 250 }catch(int zero) {
246 // we throw zero in case we're ensuring that this very process is running. 251 // we throw zero in case we're ensuring that this very process is running.
247 // we don't have to daemonize if we're daemonic. 252 // we don't have to daemonize if we're daemonic.
diff --git a/src/process.cc b/src/process.cc
index 1ffac9f..8a5b5d2 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,43 +1,59 @@
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 <sys/wait.h> 8#include <sys/wait.h>
8#include <syslog.h> 9#include <syslog.h>
9#include <errno.h> 10#include <errno.h>
10#include <iostream> 11#include <iostream>
11#include <fstream> 12#include <fstream>
13#include <sstream>
12#include <stdexcept> 14#include <stdexcept>
15#include <string>
16#include <vector>
13using namespace std; 17using namespace std;
14#include "process.h" 18#include "process.h"
15#include "configuration.h" 19#include "configuration.h"
16 20
21static multimap<string,pid_t> procpids;
22
23void process::check() const {
24 if(!pidfile.empty()) {
25 signal(0);
26 }else if(!process_name.empty()) {
27 if(procpids.empty())
28 gather_proc_info();
29 if(procpids.find(process_name)==procpids.end())
30 throw runtime_error("no such process");
31 } // XXX: or else?
32}
17void process::check(const string& id,configuration& config) { 33void process::check(const string& id,configuration& config) {
18 try { 34 try {
19 signal(0); 35 check();
20 patience = 0; 36 patience = 0;
21 }catch(exception& e) { 37 }catch(exception& e) {
22 if(patience>60) { // TODO: configurable 38 if(patience>60) { // TODO: configurable
23 patience = 0; 39 patience = 0;
24 }else{ 40 }else{
25 if(patience<10) { // TODO: configurable 41 if(patience<10) { // TODO: configurable
26 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str()); 42 syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str());
27 do_notify(id,"Starting up", 43 do_notify(id,"Starting up",
28 "The named process seems to be down. Dudki will try\n" 44 "The named process seems to be down. Dudki will try\n"
29 "to revive it by running the specified command.\n", 45 "to revive it by running the specified command.\n",
30 config); 46 config);
31 try { 47 try {
32 launch(id,config); 48 launch(id,config);
33 }catch(exception& e) { 49 }catch(exception& e) {
34 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 50 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
35 } 51 }
36 }else if(patience==10){ // TODO: configurable like the above 52 }else if(patience==10){ // TODO: configurable like the above
37 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str()); 53 syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str());
38 do_notify(id,"Giving up", 54 do_notify(id,"Giving up",
39 "After a number of attempts to relaunch the named process\n" 55 "After a number of attempts to relaunch the named process\n"
40 "It still seems to be down. Dudki is giving up attempts\n" 56 "It still seems to be down. Dudki is giving up attempts\n"
41 "to revive the process for a while.\n", 57 "to revive the process for a while.\n",
42 config); 58 config);
43 } 59 }
@@ -155,35 +171,115 @@ void process::notify_mailto(const string& email,const string& id,const string& e
155 close(files[0]); 171 close(files[0]);
156 FILE *mta = fdopen(files[1],"w"); 172 FILE *mta = fdopen(files[1],"w");
157 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { 173 for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) {
158 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 174 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
159 } 175 }
160 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { 176 for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) {
161 if(mailto_headers.find(i->first)!=mailto_headers.end()) 177 if(mailto_headers.find(i->first)!=mailto_headers.end())
162 continue; 178 continue;
163 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); 179 fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str());
164 } 180 }
165 fprintf(mta, 181 fprintf(mta,
166 "Subject: [%s] %s\n\n" 182 "Subject: [%s] %s\n\n"
167 "%s\n" 183 "%s\n"
168 "---\n" 184 "---\n"
169 "This message was sent automatically by the 'dudki' daemon\n", 185 "This message was sent automatically by the 'dudki' daemon\n",
170 id.c_str(), event.c_str(), 186 id.c_str(), event.c_str(),
171 description.c_str() ); 187 description.c_str() );
172 fclose(mta); 188 fclose(mta);
173 int status; 189 int status;
174 waitpid(pid,&status,0); 190 waitpid(pid,&status,0);
175 // TODO: check the return code 191 // TODO: check the return code
176} 192}
177 193
178void process::signal(int signum) const { 194void process::signal(int signum) const {
195 if(!pidfile.empty()) {
179 ifstream pids(pidfile.c_str(),ios::in); 196 ifstream pids(pidfile.c_str(),ios::in);
180 if(!pids) 197 if(!pids)
181 throw runtime_error("no pidfile found"); 198 throw runtime_error("no pidfile found");
182 pid_t pid = 0; 199 pid_t pid = 0;
183 pids >> pid; 200 pids >> pid;
184 pids.close(); 201 pids.close();
185 if(!pid) 202 if(!pid)
186 throw runtime_error("no pid in pidfile"); 203 throw runtime_error("no pid in pidfile");
187 if(kill(pid,signum)) 204 if(kill(pid,signum))
188 throw runtime_error("failed to signal process"); 205 throw runtime_error("failed to signal process");
206 }else if(!process_name.empty()) {
207 if(procpids.empty())
208 gather_proc_info();
209 pair<multimap<string,pid_t>::const_iterator,multimap<string,pid_t>::const_iterator> range = procpids.equal_range(process_name);
210 int count = 0;
211 for(multimap<string,pid_t>::const_iterator i=range.first;i!=range.second;++i) {
212 pid_t pid = i->second;
213 if(kill(i->second,signum))
214 throw runtime_error("failed to signal process");
215 count++;
216 }
217 if(!count)
218 throw runtime_error("no running instance detected");
219 }else
220 throw runtime_error("nothing is known about the process");
221}
222
223void process::prepare_herd() {
224 procpids.clear();
225}
226void process::unprepare_herd() {
227 procpids.clear();
228}
229void process::gather_proc_info() {
230 vector<pid_t> allpids;
231 DIR *pd = opendir("/proc");
232 if(!pd)
233 throw runtime_error("failed to open /proc");
234 struct dirent *pde;
235 pid_t selfpid = getpid();
236 while(pde=readdir(pd)) {
237 errno=0;
238 pid_t pid = atoi(pde->d_name);
239 if((!pid) || pid==selfpid)
240 continue;
241 allpids.push_back(pid);
242 }
243 closedir(pd);
244 char s[256];
245 procpids.clear();
246 for(vector<pid_t>::const_iterator i=allpids.begin();i!=allpids.end();++i) {
247 int r = snprintf(s,sizeof(s),"/proc/%d/stat",*i);
248 if(r>=sizeof(s) || r<1)
249 continue;
250 string cmd;
251 ifstream ss(s,ios::in);
252 if(!ss)
253 continue;
254 getline(ss,cmd);
255 string::size_type op = cmd.find('(');
256 if(op==string::npos)
257 continue;
258 cmd.erase(0,op+1);
259 string::size_type cp = cmd.find(')');
260 if(cp==string::npos)
261 continue;
262 cmd.erase(cp);
263 r = snprintf(s,sizeof(s),"/proc/%d/cmdline",*i);
264 if(r>=sizeof(s) || r<1)
265 continue;
266 ifstream cs(s,ios::binary);
267 if(!cs)
268 continue;
269 string command;
270 while(cs) {
271 string cl;
272 getline(cs,cl,(char)0);
273 string::size_type lsl = cl.rfind('/');
274 if(lsl!=string::npos)
275 cl.erase(0,lsl+1);
276 if(cl.substr(0,cmd.length())==cmd) {
277 command = cl;
278 break;
279 }
280 }
281 procpids.insert(pair<string,pid_t>(cmd,*i));
282 if((!command.empty()) && cmd!=command)
283 procpids.insert(pair<string,pid_t>(command,*i));
284 }
189} 285}
diff --git a/src/process.h b/src/process.h
index 27ee049..90b12d9 100644
--- a/src/process.h
+++ b/src/process.h
@@ -1,38 +1,44 @@
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 process_name;
15 string restart_cmd; 16 string restart_cmd;
16 string notify; 17 string notify;
17 string user; 18 string user;
18 string group; 19 string group;
19 string chroot; 20 string chroot;
20 headers_t mailto_headers; 21 headers_t mailto_headers;
21 22
22 int patience; 23 int patience;
23 24
24 process() 25 process()
25 : patience(0) { } 26 : patience(0) { }
26 27
27 void check(const string& id,configuration& config); 28 void check(const string& id,configuration& config);
28 void launch(const string& id,configuration& config); 29 void launch(const string& id,configuration& config);
29 void do_notify(const string& id,const string& event,const string& description,configuration& config); 30 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, 31 void notify_mailto(const string& email,const string& id,const string& event,
31 const string& description,configuration& config); 32 const string& description,configuration& config);
32 33
33 void signal(int signum) const; 34 void signal(int signum) const;
35 void check() const;
36
37 static void prepare_herd();
38 static void gather_proc_info();
39 static void unprepare_herd();
34}; 40};
35 41
36typedef map<string,process> processes_t; 42typedef map<string,process> processes_t;
37 43
38#endif /* __PROCESS_H */ 44#endif /* __PROCESS_H */