summaryrefslogtreecommitdiffabout
path: root/src
Unidiff
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/configuration.cc5
-rw-r--r--src/dudki.cc7
-rw-r--r--src/process.cc118
-rw-r--r--src/process.h6
4 files changed, 124 insertions, 12 deletions
diff --git a/src/configuration.cc b/src/configuration.cc
index eb010c1..edc8c04 100644
--- a/src/configuration.cc
+++ b/src/configuration.cc
@@ -1,149 +1,154 @@
1#include <stdexcept> 1#include <stdexcept>
2using namespace std; 2using namespace std;
3#include <dotconf.h> 3#include <dotconf.h>
4#include "configuration.h" 4#include "configuration.h"
5 5
6#ifndef DEFAULT_PID_FILE 6#ifndef DEFAULT_PID_FILE
7# define DEFAULT_PID_FILE "/var/run/dudki.pid" 7# define DEFAULT_PID_FILE "/var/run/dudki.pid"
8#endif 8#endif
9 9
10configuration::configuration() 10configuration::configuration()
11 : check_interval(60), pidfile(DEFAULT_PID_FILE), 11 : check_interval(60), pidfile(DEFAULT_PID_FILE),
12 daemonize(true) { 12 daemonize(true) {
13 } 13 }
14 14
15enum dc_ctx { 15enum dc_ctx {
16 DCC_ROOT = 1, 16 DCC_ROOT = 1,
17 DCC_PROCESS = 2 17 DCC_PROCESS = 2
18}; 18};
19struct dc_context { 19struct dc_context {
20 dc_ctx ctx; 20 dc_ctx ctx;
21 configuration* cf; 21 configuration* cf;
22 process* ps; 22 process* ps;
23 23
24 dc_context() 24 dc_context()
25 : ctx(DCC_ROOT), cf(NULL), ps(NULL) { } 25 : ctx(DCC_ROOT), cf(NULL), ps(NULL) { }
26}; 26};
27 27
28static DOTCONF_CB(dco_check_interval) { dc_context *dcc = (dc_context*)ctx; 28static DOTCONF_CB(dco_check_interval) { dc_context *dcc = (dc_context*)ctx;
29 dcc->cf->check_interval = cmd->data.value; 29 dcc->cf->check_interval = cmd->data.value;
30 return NULL; 30 return NULL;
31} 31}
32static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*)ctx; 32static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*)ctx;
33 dcc->cf->daemonize = cmd->data.value; 33 dcc->cf->daemonize = cmd->data.value;
34 return NULL; 34 return NULL;
35} 35}
36 36
37static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx; 37static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx;
38 switch(dcc->ctx) { 38 switch(dcc->ctx) {
39 case DCC_ROOT: 39 case DCC_ROOT:
40 dcc->cf->pidfile = cmd->data.str; 40 dcc->cf->pidfile = cmd->data.str;
41 break; 41 break;
42 case DCC_PROCESS: 42 case DCC_PROCESS:
43 dcc->ps->pidfile = cmd->data.str; 43 dcc->ps->pidfile = cmd->data.str;
44 break; 44 break;
45 default: 45 default:
46 return "Unexpected PidFile"; 46 return "Unexpected PidFile";
47 } 47 }
48 return NULL; 48 return NULL;
49} 49}
50static DOTCONF_CB(dco_mailto_header) { dc_context *dcc = (dc_context*)ctx; 50static DOTCONF_CB(dco_mailto_header) { dc_context *dcc = (dc_context*)ctx;
51 if(cmd->arg_count!=2) 51 if(cmd->arg_count!=2)
52 return "Invalid number of arguments"; 52 return "Invalid number of arguments";
53 string h = cmd->data.list[0]; 53 string h = cmd->data.list[0];
54 string v = cmd->data.list[1]; 54 string v = cmd->data.list[1];
55 switch(dcc->ctx) { 55 switch(dcc->ctx) {
56 case DCC_ROOT: 56 case DCC_ROOT:
57 dcc->cf->mailto_headers[h] = v; 57 dcc->cf->mailto_headers[h] = v;
58 break; 58 break;
59 case DCC_PROCESS: 59 case DCC_PROCESS:
60 dcc->ps->mailto_headers[h] = v; 60 dcc->ps->mailto_headers[h] = v;
61 break; 61 break;
62 default: 62 default:
63 return "Unexpected MailtoHeader"; 63 return "Unexpected MailtoHeader";
64 } 64 }
65 return NULL; 65 return NULL;
66} 66}
67static DOTCONF_CB(dco_notify) { dc_context *dcc = (dc_context*)ctx; 67static DOTCONF_CB(dco_notify) { dc_context *dcc = (dc_context*)ctx;
68 switch(dcc->ctx) { 68 switch(dcc->ctx) {
69 case DCC_ROOT: 69 case DCC_ROOT:
70 dcc->cf->notify = cmd->data.str; 70 dcc->cf->notify = cmd->data.str;
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)
143 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_create()"); 148 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_create()");
144 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler; 149 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
145 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker; 150 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
146 if(!dotconf_command_loop(cf)) 151 if(!dotconf_command_loop(cf))
147 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_command_loop()"); 152 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_command_loop()");
148 dotconf_cleanup(cf); 153 dotconf_cleanup(cf);
149} 154}
diff --git a/src/dudki.cc b/src/dudki.cc
index 9562079..c966695 100644
--- a/src/dudki.cc
+++ b/src/dudki.cc
@@ -1,287 +1,292 @@
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>
8using namespace std; 8using namespace std;
9#include "configuration.h" 9#include "configuration.h"
10#include "util.h" 10#include "util.h"
11 11
12#include "config.h" 12#include "config.h"
13#ifdef HAVE_GETOPT_H 13#ifdef HAVE_GETOPT_H
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 {
64 op_default, 66 op_default,
65 op_work, 67 op_work,
66 op_signal, 68 op_signal,
67 op_ensure, 69 op_ensure,
68 op_test 70 op_test
69 } op = op_default; 71 } op = op_default;
70 int op_signum = 0; 72 int op_signum = 0;
71 while(true) { 73 while(true) {
72 #defineSHORTOPTSTRING "f:hVLrkcets:" 74 #defineSHORTOPTSTRING "f:hVLrkcets:"
73#ifdef HAVE_GETOPT_LONG 75#ifdef HAVE_GETOPT_LONG
74 static struct option opts[] = { 76 static struct option opts[] = {
75 { "help", no_argument, 0, 'h' }, 77 { "help", no_argument, 0, 'h' },
76 { "usage", no_argument, 0, 'h' }, 78 { "usage", no_argument, 0, 'h' },
77 { "version", no_argument, 0, 'V' }, 79 { "version", no_argument, 0, 'V' },
78 { "license", no_argument, 0, 'L' }, 80 { "license", no_argument, 0, 'L' },
79 { "config", required_argument, 0, 'f' }, 81 { "config", required_argument, 0, 'f' },
80 { "kill", no_argument, 0, 'k' }, 82 { "kill", no_argument, 0, 'k' },
81 { "reload", no_argument, 0, 'r' }, 83 { "reload", no_argument, 0, 'r' },
82 { "signal", required_argument, 0, 's' }, 84 { "signal", required_argument, 0, 's' },
83 { "check", no_argument, 0, 'c' }, 85 { "check", no_argument, 0, 'c' },
84 { "ensure", no_argument, 0, 'e' }, 86 { "ensure", no_argument, 0, 'e' },
85 { "test", no_argument, 0, 't' }, 87 { "test", no_argument, 0, 't' },
86 { NULL, 0, 0, 0 } 88 { NULL, 0, 0, 0 }
87 }; 89 };
88 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); 90 int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL);
89#else /* !HAVE_GETOPT_LONG */ 91#else /* !HAVE_GETOPT_LONG */
90 int c = getopt(argc,argv,SHORTOPTSTRING); 92 int c = getopt(argc,argv,SHORTOPTSTRING);
91#endif /* /HAVE_GETOPT_LONG */ 93#endif /* /HAVE_GETOPT_LONG */
92 if(c==-1) 94 if(c==-1)
93 break; 95 break;
94 switch(c) { 96 switch(c) {
95 case 'h': 97 case 'h':
96 cerr << PHEADER << endl 98 cerr << PHEADER << endl
97 << PCOPY << endl << endl 99 << PCOPY << endl << endl
98 << " " << argv[0] << " [options] [processes]" << endl << endl << 100 << " " << argv[0] << " [options] [processes]" << endl << endl <<
99#ifdef HAVE_GETOPT_LONG 101#ifdef HAVE_GETOPT_LONG
100 " -h, --help\n" 102 " -h, --help\n"
101 " --usage display this text\n" 103 " --usage display this text\n"
102 " -V, --version display version number\n" 104 " -V, --version display version number\n"
103 " -L, --license show license\n" 105 " -L, --license show license\n"
104 " -f filename, --config=filename\n" 106 " -f filename, --config=filename\n"
105 " specify the configuration file to use\n" 107 " specify the configuration file to use\n"
106 "\n" 108 "\n"
107 " -k, --kill stop running instance (send SIGTERM)\n" 109 " -k, --kill stop running instance (send SIGTERM)\n"
108 " -r, --reload reload running instance (send SIGHUP)\n" 110 " -r, --reload reload running instance (send SIGHUP)\n"
109 " -s signum, --signal=signum\n" 111 " -s signum, --signal=signum\n"
110 " send the specified signal to the running process\n" 112 " send the specified signal to the running process\n"
111 " -c, --check check if the process is running\n" 113 " -c, --check check if the process is running\n"
112 " (the above commands operate on dudki itself if no\n" 114 " (the above commands operate on dudki itself if no\n"
113 " process name has been specified)\n" 115 " process name has been specified)\n"
114 " -e, --ensure ensure that dudki is running\n" 116 " -e, --ensure ensure that dudki is running\n"
115 " -t, --test test configuration file and exit" 117 " -t, --test test configuration file and exit"
116#else /* !HAVE_GETOPT_LONG */ 118#else /* !HAVE_GETOPT_LONG */
117 " -h display this text\n" 119 " -h display this text\n"
118 " -V display version number\n" 120 " -V display version number\n"
119 " -L show license\n" 121 " -L show license\n"
120 " -f filename specify the configuration file to use\n" 122 " -f filename specify the configuration file to use\n"
121 "\n" 123 "\n"
122 " -k stop running instance (send SIGTERM)\n" 124 " -k stop running instance (send SIGTERM)\n"
123 " -r reload running instance (send SIGHUP)\n" 125 " -r reload running instance (send SIGHUP)\n"
124 " -s signum send the specified signal to the running process\n" 126 " -s signum send the specified signal to the running process\n"
125 " -c check if the process is running\n" 127 " -c check if the process is running\n"
126 " (the above commands operate on dudki itself if no\n" 128 " (the above commands operate on dudki itself if no\n"
127 " process name has been specified)\n" 129 " process name has been specified)\n"
128 " -e ensure that dudki is running\n" 130 " -e ensure that dudki is running\n"
129 " -t test configuration file and exit" 131 " -t test configuration file and exit"
130#endif /* /HAVE_GETOPT_LONG */ 132#endif /* /HAVE_GETOPT_LONG */
131 << endl; 133 << endl;
132 exit(0); 134 exit(0);
133 break; 135 break;
134 case 'V': 136 case 'V':
135 cerr << VERSION << endl; 137 cerr << VERSION << endl;
136 exit(0); 138 exit(0);
137 break; 139 break;
138 case 'L': 140 case 'L':
139 extern const char *COPYING; 141 extern const char *COPYING;
140 cerr << COPYING << endl; 142 cerr << COPYING << endl;
141 exit(0); 143 exit(0);
142 break; 144 break;
143 case 'f': 145 case 'f':
144 config_file = optarg; 146 config_file = optarg;
145 break; 147 break;
146 case 'k': 148 case 'k':
147 if(op!=op_default) { 149 if(op!=op_default) {
148 cerr << "Can't obey two or more orders at once" << endl; 150 cerr << "Can't obey two or more orders at once" << endl;
149 exit(1); 151 exit(1);
150 } 152 }
151 op = op_signal; op_signum = SIGTERM; 153 op = op_signal; op_signum = SIGTERM;
152 break; 154 break;
153 case 'r': 155 case 'r':
154 if(op!=op_default) { 156 if(op!=op_default) {
155 cerr << "Can't obey two or more orders at once" << endl; 157 cerr << "Can't obey two or more orders at once" << endl;
156 exit(1); 158 exit(1);
157 } 159 }
158 op = op_signal; op_signum = SIGHUP; 160 op = op_signal; op_signum = SIGHUP;
159 break; 161 break;
160 case 'c': 162 case 'c':
161 if(op!=op_default) { 163 if(op!=op_default) {
162 cerr << "Can't obey two or more orders at once" << endl; 164 cerr << "Can't obey two or more orders at once" << endl;
163 exit(1); 165 exit(1);
164 } 166 }
165 op = op_signal; op_signum = 0; 167 op = op_signal; op_signum = 0;
166 break; 168 break;
167 case 'e': 169 case 'e':
168 if(op!=op_default) { 170 if(op!=op_default) {
169 cerr << "Can't obey two or more orders at once" << endl; 171 cerr << "Can't obey two or more orders at once" << endl;
170 exit(1); 172 exit(1);
171 } 173 }
172 op = op_ensure; 174 op = op_ensure;
173 break; 175 break;
174 case 't': 176 case 't':
175 if(op!=op_default) { 177 if(op!=op_default) {
176 cerr << "Can't obey two or more orders at once" << endl; 178 cerr << "Can't obey two or more orders at once" << endl;
177 exit(1); 179 exit(1);
178 } 180 }
179 op = op_test; 181 op = op_test;
180 break; 182 break;
181 case 's': 183 case 's':
182 if(op!=op_default) { 184 if(op!=op_default) {
183 cerr << "Can't obey two or more orders at once" << endl; 185 cerr << "Can't obey two or more orders at once" << endl;
184 exit(1); 186 exit(1);
185 } 187 }
186 op = op_signal; 188 op = op_signal;
187 errno = 0; 189 errno = 0;
188 op_signum = strtol(optarg,NULL,0); 190 op_signum = strtol(optarg,NULL,0);
189 if(errno) { 191 if(errno) {
190 cerr << "Can't obtain the signal value" << endl; 192 cerr << "Can't obtain the signal value" << endl;
191 exit(1); 193 exit(1);
192 } 194 }
193 break; 195 break;
194 default: 196 default:
195 cerr << "Huh??" << endl; 197 cerr << "Huh??" << endl;
196 exit(1); 198 exit(1);
197 break; 199 break;
198 } 200 }
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");
223 i->second.signal(op_signum); 225 if(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.
248 config.daemonize = false; 253 config.daemonize = false;
249 } 254 }
250 case op_default: 255 case op_default:
251 case op_work: 256 case op_work:
252 { 257 {
253 if(config.daemonize) { 258 if(config.daemonize) {
254 pid_t pf = fork(); 259 pid_t pf = fork();
255 if(pf<0) 260 if(pf<0)
256 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 261 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
257 if(pf) { 262 if(pf) {
258 _exit(0); 263 _exit(0);
259 } 264 }
260 } 265 }
261 pid_file pidfile; 266 pid_file pidfile;
262 pidfile.set(config.pidfile); 267 pidfile.set(config.pidfile);
263 signal(SIGINT,lethal_signal_handler); 268 signal(SIGINT,lethal_signal_handler);
264 signal(SIGABRT,lethal_signal_handler); 269 signal(SIGABRT,lethal_signal_handler);
265 signal(SIGTERM,lethal_signal_handler); 270 signal(SIGTERM,lethal_signal_handler);
266 signal(SIGHUP,sighup_handler); 271 signal(SIGHUP,sighup_handler);
267 sigset_t sset; 272 sigset_t sset;
268 sigemptyset(&sset); 273 sigemptyset(&sset);
269 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT); 274 sigaddset(&sset,SIGINT); sigaddset(&sset,SIGABRT);
270 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP); 275 sigaddset(&sset,SIGTERM); sigaddset(&sset,SIGHUP);
271 sigprocmask(SIG_UNBLOCK,&sset,NULL); 276 sigprocmask(SIG_UNBLOCK,&sset,NULL);
272 while(!finishing) { 277 while(!finishing) {
273 check_herd(config); 278 check_herd(config);
274 sleep(config.check_interval); 279 sleep(config.check_interval);
275 } 280 }
276 if(restarting) 281 if(restarting)
277 execvp(_argv[0],_argv); 282 execvp(_argv[0],_argv);
278 } 283 }
279 break; 284 break;
280 default: 285 default:
281 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); 286 throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error");
282 } 287 }
283 }catch(exception& e) { 288 }catch(exception& e) {
284 cerr << "Oops: " << e.what() << endl; 289 cerr << "Oops: " << e.what() << endl;
285 return 1; 290 return 1;
286 } 291 }
287} 292}
diff --git a/src/process.cc b/src/process.cc
index 1ffac9f..8a5b5d2 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -1,189 +1,285 @@
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 }
44 patience++; 60 patience++;
45 } 61 }
46 } 62 }
47} 63}
48 64
49void process::launch(const string& id,configuration& config) { 65void process::launch(const string& id,configuration& config) {
50 uid_t uid = (uid_t)-1; 66 uid_t uid = (uid_t)-1;
51 gid_t gid = (gid_t)-1; 67 gid_t gid = (gid_t)-1;
52 if(!user.empty()) { 68 if(!user.empty()) {
53 struct passwd *ptmp = getpwnam(user.c_str()); 69 struct passwd *ptmp = getpwnam(user.c_str());
54 if(ptmp) { 70 if(ptmp) {
55 uid = ptmp->pw_uid; 71 uid = ptmp->pw_uid;
56 gid = ptmp->pw_gid; 72 gid = ptmp->pw_gid;
57 }else{ 73 }else{
58 errno=0; 74 errno=0;
59 uid = strtol(user.c_str(),NULL,0); 75 uid = strtol(user.c_str(),NULL,0);
60 if(errno) 76 if(errno)
61 throw runtime_error("Failed to resolve User value to uid"); 77 throw runtime_error("Failed to resolve User value to uid");
62 } 78 }
63 } 79 }
64 if(!group.empty()) { 80 if(!group.empty()) {
65 struct group *gtmp = getgrnam(group.c_str()); 81 struct group *gtmp = getgrnam(group.c_str());
66 if(gtmp) { 82 if(gtmp) {
67 gid = gtmp->gr_gid; 83 gid = gtmp->gr_gid;
68 }else{ 84 }else{
69 errno = 0; 85 errno = 0;
70 gid = strtol(group.c_str(),NULL,0); 86 gid = strtol(group.c_str(),NULL,0);
71 if(errno) 87 if(errno)
72 throw runtime_error("Failed to reslove Group value to gid"); 88 throw runtime_error("Failed to reslove Group value to gid");
73 } 89 }
74 } 90 }
75 pid_t p = fork(); 91 pid_t p = fork();
76 if(p<0) 92 if(p<0)
77 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); 93 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()");
78 if(!p) { 94 if(!p) {
79 // child 95 // child
80 try { 96 try {
81 setsid(); 97 setsid();
82 if(user.empty()) { 98 if(user.empty()) {
83 if((getgid()!=gid) && setgid(gid)) 99 if((getgid()!=gid) && setgid(gid))
84 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); 100 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()");
85 }else{ 101 }else{
86 if(initgroups(user.c_str(),gid)) 102 if(initgroups(user.c_str(),gid))
87 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()"); 103 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to initgroups()");
88 } 104 }
89 if(!chroot.empty()) { 105 if(!chroot.empty()) {
90 if(::chroot(chroot.c_str())) 106 if(::chroot(chroot.c_str()))
91 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); 107 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()");
92 } 108 }
93 if(!user.empty()) { 109 if(!user.empty()) {
94 if((getuid()!=uid) && setuid(uid)) 110 if((getuid()!=uid) && setuid(uid))
95 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); 111 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()");
96 } 112 }
97 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; 113 char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL };
98 close(0); close(1); close(2); 114 close(0); close(1); close(2);
99 execv("/bin/sh",argv); 115 execv("/bin/sh",argv);
100 }catch(exception& e) { 116 }catch(exception& e) {
101 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); 117 syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what());
102 } 118 }
103 _exit(-1); 119 _exit(-1);
104 } 120 }
105 // parent 121 // parent
106 int rv; 122 int rv;
107 if(waitpid(p,&rv,0)<0) 123 if(waitpid(p,&rv,0)<0)
108 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); 124 throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()");
109} 125}
110 126
111void process::do_notify(const string& id,const string& event,const string& description,configuration& config) { 127void process::do_notify(const string& id,const string& event,const string& description,configuration& config) {
112 string the_notify; 128 string the_notify;
113 if(!notify.empty()) 129 if(!notify.empty())
114 the_notify=notify; 130 the_notify=notify;
115 else if(!config.notify.empty()) 131 else if(!config.notify.empty())
116 the_notify=config.notify; 132 the_notify=config.notify;
117 else 133 else
118 return; 134 return;
119 try { 135 try {
120 string::size_type colon = the_notify.find(':'); 136 string::size_type colon = the_notify.find(':');
121 if(colon==string::npos) 137 if(colon==string::npos)
122 throw runtime_error("invalid notify action specification"); 138 throw runtime_error("invalid notify action specification");
123 string nschema = the_notify.substr(0,colon); 139 string nschema = the_notify.substr(0,colon);
124 string ntarget = the_notify.substr(colon+1); 140 string ntarget = the_notify.substr(colon+1);
125 if(nschema=="mailto") { 141 if(nschema=="mailto") {
126 notify_mailto(ntarget,id,event,description,config); 142 notify_mailto(ntarget,id,event,description,config);
127 }else 143 }else
128 throw runtime_error("unrecognized notification schema"); 144 throw runtime_error("unrecognized notification schema");
129 }catch(exception& e) { 145 }catch(exception& e) {
130 syslog(LOG_ERR,"Notification error: %s",e.what()); 146 syslog(LOG_ERR,"Notification error: %s",e.what());
131 } 147 }
132} 148}
133 149
134void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) { 150void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) {
135 int files[2]; 151 int files[2];
136 if(pipe(files)) 152 if(pipe(files))
137 throw runtime_error("Failed to pipe()"); 153 throw runtime_error("Failed to pipe()");
138 pid_t pid = vfork(); 154 pid_t pid = vfork();
139 if(pid==-1) { 155 if(pid==-1) {
140 close(files[0]); 156 close(files[0]);
141 close(files[1]); 157 close(files[1]);
142 throw runtime_error("Failed to vfork()"); 158 throw runtime_error("Failed to vfork()");
143 } 159 }
144 if(!pid) { 160 if(!pid) {
145 // child 161 // child
146 if(dup2(files[0],0)!=0) 162 if(dup2(files[0],0)!=0)
147 _exit(-1); 163 _exit(-1);
148 close(1); 164 close(1);
149 close(files[0]); 165 close(files[0]);
150 close(files[1]); 166 close(files[1]);
151 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL); 167 execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL);
152 _exit(-1); 168 _exit(-1);
153 } 169 }
154 // parent 170 // parent
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 {
179 ifstream pids(pidfile.c_str(),ios::in); 195 if(!pidfile.empty()) {
180 if(!pids) 196 ifstream pids(pidfile.c_str(),ios::in);
181 throw runtime_error("no pidfile found"); 197 if(!pids)
182 pid_t pid = 0; 198 throw runtime_error("no pidfile found");
183 pids >> pid; 199 pid_t pid = 0;
184 pids.close(); 200 pids >> pid;
185 if(!pid) 201 pids.close();
186 throw runtime_error("no pid in pidfile"); 202 if(!pid)
187 if(kill(pid,signum)) 203 throw runtime_error("no pid in pidfile");
188 throw runtime_error("failed to signal process"); 204 if(kill(pid,signum))
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 */