summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2004-07-24 00:24:07 (UTC)
committer Michael Krelin <hacker@klever.net>2004-07-24 00:24:07 (UTC)
commitfbc32792b8d8266ff90aa60403f5da78739236f4 (patch) (unidiff)
tree77bc3f196a3733c9c86290f8a73d60bb609bbdd5
parent125671c860a82643d36bc3da279d0b831fae4b34 (diff)
downloaddudki-fbc32792b8d8266ff90aa60403f5da78739236f4.zip
dudki-fbc32792b8d8266ff90aa60403f5da78739236f4.tar.gz
dudki-fbc32792b8d8266ff90aa60403f5da78739236f4.tar.bz2
processes specified by process names (pidof-like).
Diffstat (more/less context) (ignore 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.cc7
-rw-r--r--src/process.cc118
-rw-r--r--src/process.h6
6 files changed, 131 insertions, 14 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,154 +1,158 @@
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
26Specifies interval in seconds at wich dudki performs checks. 26Specifies interval in seconds at wich dudki performs checks.
27.TP 27.TP
28\fBDaemonize\fR \fIon/off\fR 28\fBDaemonize\fR \fIon/off\fR
29Specifies whether dudki daemon should fork itself into background. 29Specifies whether dudki daemon should fork itself into background.
30Normally, you don't want it to run in foreground. Unless you want to run 30Normally, you don't want it to run in foreground. Unless you want to run
31it from \fIinit(8)\fR process (I've nevetr tried it) via 31it from \fIinit(8)\fR process (I've nevetr tried it) via
32\fIinittab(5)\fR. 32\fIinittab(5)\fR.
33.TP 33.TP
34\fBPidFile\fR \fIfilename\fR 34\fBPidFile\fR \fIfilename\fR
35Specifies where to store dudki's process id (default is 35Specifies where to store dudki's process id (default is
36/var/run/dudki.pid) 36/var/run/dudki.pid)
37.TP 37.TP
38\fBMailtoHeader\fR \fIheader\fR \fIcontent\fR 38\fBMailtoHeader\fR \fIheader\fR \fIcontent\fR
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
87\fB</Process>\fR 91\fB</Process>\fR
88Signifies the end of process section. 92Signifies the end of process section.
89 93
90.SH EXAMPLE 94.SH EXAMPLE
91 95
92.br 96.br
93CheckInterval 60 97CheckInterval 60
94.br 98.br
95PidFile /var/run/dudki.pid 99PidFile /var/run/dudki.pid
96.br 100.br
97Daemonize on 101Daemonize on
98.br 102.br
99MailtoHeader From "Dudki <root@klever.net>" 103MailtoHeader From "Dudki <root@klever.net>"
100.br 104.br
101MailtoHeader Reply-To devnull@klever.net 105MailtoHeader Reply-To devnull@klever.net
102.br 106.br
103Notify mailto:hacker@klever.net 107Notify mailto:hacker@klever.net
104.br 108.br
105 109
106.br 110.br
107<Process apache> 111<Process apache>
108.br 112.br
109 PidFile /var/run/httpd.pid 113 PidFile /var/run/httpd.pid
110.br 114.br
111 RestartCommand "exec /usr/sbin/apachectl start" 115 RestartCommand "exec /usr/sbin/apachectl start"
112.br 116.br
113</Process> 117</Process>
114.br 118.br
115<Process named> 119<Process named>
116.br 120.br
117 PidFile /var/run/named.pid 121 PidFile /var/run/named.pid
118.br 122.br
119 RestartCommand "exec /usr/sbin/named" 123 RestartCommand "exec /usr/sbin/named"
120.br 124.br
121</Process> 125</Process>
122 126
123.SH AUTHOR 127.SH AUTHOR
124 128
125Written by Michael 'hacker' Krelin <hacker@klever.net> 129Written by Michael 'hacker' Krelin <hacker@klever.net>
126 130
127.SH COPYRIGHT 131.SH COPYRIGHT
128 132
129Copyright (c) 2004 Klever Group (http://www.klever.net/) 133Copyright (c) 2004 Klever Group (http://www.klever.net/)
130 134
131Permission is hereby granted, free of charge, to any person obtaining a copy of 135Permission is hereby granted, free of charge, to any person obtaining a copy of
132this software and associated documentation files (the "Software"), to deal in 136this software and associated documentation files (the "Software"), to deal in
133the Software without restriction, including without limitation the rights to 137the Software without restriction, including without limitation the rights to
134use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 138use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
135of the Software, and to permit persons to whom the Software is furnished to do 139of the Software, and to permit persons to whom the Software is furnished to do
136so, subject to the following conditions: 140so, subject to the following conditions:
137 141
138The above copyright notice and this permission notice shall be included in all 142The above copyright notice and this permission notice shall be included in all
139copies or substantial portions of the Software. 143copies or substantial portions of the Software.
140 144
141THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 145THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
142IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 146IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
143FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 147FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
144AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 148AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
145LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 149LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
146OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 150OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
147SOFTWARE. 151SOFTWARE.
148 152
149.SH BUGS 153.SH BUGS
150 154
151You tell me. Send reports to <dudki-bugs@klever.net> 155You tell me. Send reports to <dudki-bugs@klever.net>
152 156
153.SH SEE ALSO 157.SH SEE ALSO
154\fBdudki\fR(8), \fBinit\fR(8), \fBinittab\fR(5) 158\fBdudki\fR(8), \fBinit\fR(8), \fBinittab\fR(5)
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 */