-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | COPYING | 19 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | README | 0 | ||||
-rwxr-xr-x | autogen.sh | 6 | ||||
-rw-r--r-- | configure.ac | 38 | ||||
-rw-r--r-- | man/.gitignore | 4 | ||||
-rw-r--r-- | man/Makefile.am | 1 | ||||
-rw-r--r-- | man/dudki.8.in | 123 | ||||
-rw-r--r-- | man/dudki.conf.5.in | 154 | ||||
-rw-r--r-- | src/.gitignore | 5 | ||||
-rw-r--r-- | src/Makefile.am | 18 | ||||
-rw-r--r-- | src/configuration.cc | 149 | ||||
-rw-r--r-- | src/configuration.h | 23 | ||||
-rw-r--r-- | src/dudki.cc | 244 | ||||
-rw-r--r-- | src/process.cc | 184 | ||||
-rw-r--r-- | src/process.h | 36 | ||||
-rw-r--r-- | src/util.cc | 21 | ||||
-rw-r--r-- | src/util.h | 20 |
21 files changed, 1051 insertions, 0 deletions
@@ -0,0 +1,3 @@ | |||
1 | Klever dissected: | ||
2 | Michael 'hacker' Krelin <hacker@klever.net> | ||
3 | Leonid Ivanov <kamel@klever.net> | ||
@@ -0,0 +1,19 @@ | |||
1 | Copyright (c) 2004 Klever Group (http://www.klever.net/) | ||
2 | |||
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
4 | this software and associated documentation files (the "Software"), to deal in | ||
5 | the Software without restriction, including without limitation the rights to | ||
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
7 | of the Software, and to permit persons to whom the Software is furnished to do | ||
8 | so, subject to the following conditions: | ||
9 | |||
10 | The above copyright notice and this permission notice shall be included in all | ||
11 | copies or substantial portions of the Software. | ||
12 | |||
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | SOFTWARE. | ||
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/ChangeLog | |||
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9f0d419 --- a/dev/null +++ b/Makefile.am | |||
@@ -0,0 +1 @@ | |||
SUBDIRS = src man | |||
@@ -0,0 +1,2 @@ | |||
1 | 0.0 | ||
2 | - Initial release. | ||
diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d4afeb4 --- a/dev/null +++ b/autogen.sh | |||
@@ -0,0 +1,6 @@ | |||
1 | #!/bin/sh | ||
2 | aclocal \ | ||
3 | && autoheader \ | ||
4 | && automake -a \ | ||
5 | && autoconf \ | ||
6 | && ./configure "$@" | ||
diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..8a2a10b --- a/dev/null +++ b/configure.ac | |||
@@ -0,0 +1,38 @@ | |||
1 | AC_INIT([dudki], [0.0], [dudki-bugs@klever.net]) | ||
2 | AC_CONFIG_SRCDIR([src/dudki.cc]) | ||
3 | AC_CONFIG_HEADER([config.h]) | ||
4 | AM_INIT_AUTOMAKE([dist-bzip2]) | ||
5 | |||
6 | AC_PROG_CXX | ||
7 | AC_PROG_CC | ||
8 | |||
9 | AC_HEADER_SYS_WAIT | ||
10 | AC_CHECK_HEADERS([syslog.h unistd.h getopt.h]) | ||
11 | |||
12 | AC_HEADER_STDBOOL | ||
13 | AC_C_CONST | ||
14 | AC_TYPE_UID_T | ||
15 | AC_TYPE_PID_T | ||
16 | |||
17 | AC_FUNC_FORK | ||
18 | AC_HEADER_STDC | ||
19 | AC_TYPE_SIGNAL | ||
20 | AC_CHECK_FUNCS([dup2 strtol]) | ||
21 | AC_CHECK_FUNC([getopt_long],[ | ||
22 | AC_DEFINE([HAVE_GETOPT_LONG],[1],[Define to make use of getopt_long]) | ||
23 | AC_SUBST(HAVE_GETOPT_LONG,1) | ||
24 | ],[ | ||
25 | AC_SUBST(HAVE_GETOPT_LONG,0) | ||
26 | ]) | ||
27 | |||
28 | PKG_CHECK_MODULES(DOTCONF,[dotconf],,[ | ||
29 | AC_MSG_ERROR([no dotconf library found]) | ||
30 | ]) | ||
31 | |||
32 | AC_CONFIG_FILES([ | ||
33 | Makefile | ||
34 | src/Makefile | ||
35 | man/Makefile | ||
36 | man/dudki.8 man/dudki.conf.5 | ||
37 | ]) | ||
38 | AC_OUTPUT | ||
diff --git a/man/.gitignore b/man/.gitignore new file mode 100644 index 0000000..087e3b6 --- a/dev/null +++ b/man/.gitignore | |||
@@ -0,0 +1,4 @@ | |||
1 | Makefile | ||
2 | Makefile.in | ||
3 | dudki.8 | ||
4 | dudki.conf.5 | ||
diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..a55b255 --- a/dev/null +++ b/man/Makefile.am | |||
@@ -0,0 +1 @@ | |||
man_MANS=dudki.8 dudki.conf.5 | |||
diff --git a/man/dudki.8.in b/man/dudki.8.in new file mode 100644 index 0000000..3011034 --- a/dev/null +++ b/man/dudki.8.in | |||
@@ -0,0 +1,123 @@ | |||
1 | .TH dudki 8 "June 9th, 2004" "dudki(8)" "Klever Group (http://www.klever.net/)" | ||
2 | .hla en | ||
3 | .ds longopt @HAVE_GETOPT_LONG@ | ||
4 | |||
5 | .SH NAME | ||
6 | |||
7 | dudki \- a process monitoring daemon | ||
8 | |||
9 | .SH SYNOPSYS | ||
10 | |||
11 | \fBdudki\fR [\fB-h\fR] | ||
12 | .if \*[longopt] [\fB--help\fR] [\fB--usage\fR] | ||
13 | [\fB-V\fR] | ||
14 | .if \*[longopt] [\fB--version\fR] | ||
15 | [\fB-L\fR] | ||
16 | .if \*[longopt] [\fB--license\fR] | ||
17 | [\fB-f\fR \fIconfigfile\fR] | ||
18 | .if \*[longopt] [\fB--config=\fR\fIconfigfile\fR] | ||
19 | [\fB-k\fR] | ||
20 | .if \*[longopt] [\fB--kill\fR] | ||
21 | [\fB-r\fR] | ||
22 | .if \*[longopt] [\fB--reload\fR] | ||
23 | [\fB-c\fR] | ||
24 | .if \*[longopt] [\fB--check\fR] | ||
25 | [\fB-e\fR] | ||
26 | .if \*[longopt] [\fB--ensure\fR] | ||
27 | [\fB-t\fR] | ||
28 | .if \*[longopt] [\fB--test\fR] | ||
29 | |||
30 | .SH DESCRIPTION | ||
31 | |||
32 | dudki daemon is designed to run in the background and periodically | ||
33 | check if certain processes specified in the configuration file are | ||
34 | running. If a process is detected as dead dudki tries to restart it | ||
35 | using the command line specified in the configuration file and notifies | ||
36 | the specified contact (currently only via email). | ||
37 | |||
38 | .SH OPTIONS | ||
39 | |||
40 | .TP | ||
41 | .ie \*[longopt] \fB-f\fR \fIconfigfile\fR, \fB--config=\fR\fIconfigfile\fR | ||
42 | .el \fB-f\fR \fIconfigfile\fR | ||
43 | Specify the configuration file to use (default is | ||
44 | \fI@sysconfdir@/dudki.conf\fR). | ||
45 | .TP | ||
46 | .ie \*[longopt] \fB-k\fR, \fB--kill\fR | ||
47 | .el \fB-k\fR | ||
48 | Stop the running instance by sending the \fBSIGTERM\fR signal. | ||
49 | .TP | ||
50 | .ie \*[longopt] \fB-r\fR, \fB--reload\fR | ||
51 | .el \fB-r\fR | ||
52 | Reload the running instance by sending the \fBSIGHUP\fR signal. | ||
53 | .TP | ||
54 | .ie \*[longopt] \fB-c\fR, \fB--check\fR | ||
55 | .el \fB-c\fR | ||
56 | Check if dudki is running. Exit with non-zero status if not. | ||
57 | .TP | ||
58 | .ie \*[longopt] \fB-e\fR, \fB--ensure\fR | ||
59 | .el \fB-e\fR | ||
60 | Ensure that dudki is running. Load, if not. Useful for running as a | ||
61 | cron job once in a while. If the daemon is running runs quietly | ||
62 | providing no output. | ||
63 | .TP | ||
64 | .ie \*[longopt] \fB-t\fR, \fB--test\fR | ||
65 | .el \fB-t\fR | ||
66 | Check the syntax of configuration file and exit. | ||
67 | .TP | ||
68 | .ie \*[longopt] \fB-h\fR, \fB--help\fR, \fB--usage\fR | ||
69 | .el \fB-h\fR | ||
70 | Display short usage message and exit. | ||
71 | .TP | ||
72 | .ie \*[longopt] \fB-V\fR, \fB--version\fR | ||
73 | .el \fB-V\fR | ||
74 | Report version and exit. | ||
75 | .TP | ||
76 | .ie \*[longopt] \fB-L\fR, \fB--license\fR | ||
77 | .el \fB-L\fR | ||
78 | Show licensing terms. | ||
79 | |||
80 | .SH EXIT STATUS | ||
81 | |||
82 | Zero in case of success, non-zero otherwise. | ||
83 | |||
84 | .SH FILES | ||
85 | .TP | ||
86 | @sysconfdir@/dudki.conf | ||
87 | Default configuration file. | ||
88 | .TP | ||
89 | /var/run/dudki.pid | ||
90 | Default pid-file for the dudki process. | ||
91 | |||
92 | .SH AUTHOR | ||
93 | |||
94 | Written by Michael 'hacker' Krelin <hacker@klever.net> | ||
95 | |||
96 | .SH COPYRIGHT | ||
97 | |||
98 | Copyright (c) 2004 Klever Group (http://www.klever.net/) | ||
99 | |||
100 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
101 | this software and associated documentation files (the "Software"), to deal in | ||
102 | the Software without restriction, including without limitation the rights to | ||
103 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
104 | of the Software, and to permit persons to whom the Software is furnished to do | ||
105 | so, subject to the following conditions: | ||
106 | |||
107 | The above copyright notice and this permission notice shall be included in all | ||
108 | copies or substantial portions of the Software. | ||
109 | |||
110 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
111 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
112 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
113 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
114 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
115 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
116 | SOFTWARE. | ||
117 | |||
118 | .SH BUGS | ||
119 | |||
120 | You tell me. Send reports to <dudki-bugs@klever.net> | ||
121 | |||
122 | .SH SEE ALSO | ||
123 | \fBdudki.conf\fR(5) | ||
diff --git a/man/dudki.conf.5.in b/man/dudki.conf.5.in new file mode 100644 index 0000000..23f636d --- a/dev/null +++ b/man/dudki.conf.5.in | |||
@@ -0,0 +1,154 @@ | |||
1 | .TH dudki.conf 5 "July 9th, 2004" "dudki.conf(5)" "Klever Group (http://www.klever.net/)" | ||
2 | .hla en | ||
3 | |||
4 | .SH NAME | ||
5 | |||
6 | dudki.conf \- The configuration file for the dudki process | ||
7 | monitoring daemon | ||
8 | |||
9 | .SH SYNOPSIS | ||
10 | |||
11 | The dudki.conf file is a runtime configuration file for the dudki | ||
12 | process monitoring daemon. It contains the information about the process | ||
13 | being monitored and the instruction on how to cope with the processes. | ||
14 | |||
15 | .SH FILE FORMAT | ||
16 | |||
17 | The file consist of the global configuration and per-process | ||
18 | configuration sections. The global configuration controls general | ||
19 | dudki behaviour and sets defaults for per-process configuration | ||
20 | directives. | ||
21 | |||
22 | .SH GLOBAL CONFIGURATION | ||
23 | |||
24 | .TP | ||
25 | \fBCheckInterval\fR \fIseconds\fR | ||
26 | Specifies interval in seconds at wich dudki performs checks. | ||
27 | .TP | ||
28 | \fBDaemonize\fR \fIon/off\fR | ||
29 | Specifies whether dudki daemon should fork itself into background. | ||
30 | Normally, you don't want it to run in foreground. Unless you want to run | ||
31 | it from \fIinit(8)\fR process (I've nevetr tried it) via | ||
32 | \fIinittab(5)\fR. | ||
33 | .TP | ||
34 | \fBPidFile\fR \fIfilename\fR | ||
35 | Specifies where to store dudki's process id (default is | ||
36 | /var/run/dudki.pid) | ||
37 | .TP | ||
38 | \fBMailtoHeader\fR \fIheader\fR \fIcontent\fR | ||
39 | Sets global defaults for process section's MailtoHeader directive. See | ||
40 | below. | ||
41 | .TP | ||
42 | \fBNotify\fR \fIschema\fR:\fItarget\fR | ||
43 | Sets global default for per process notification. See below. | ||
44 | |||
45 | .TP | ||
46 | \fB<Process\fR \fIidentifier\fR\fB>\fR | ||
47 | Starts per process configuration section. The process will be referenced | ||
48 | using the short descriptive name specified (for example in email | ||
49 | notifications). | ||
50 | |||
51 | .SH PER-PROCESS CONFIGURATION | ||
52 | |||
53 | Per-process configuration sections specifies the processes to monitor. | ||
54 | And parameters pertaining to the process. | ||
55 | |||
56 | .TP | ||
57 | \fBPidFile\fR \fIfilename\fR | ||
58 | Specifies the file where to fetch process id of the process being | ||
59 | monitored from. The absence of file, as well as the absence of process | ||
60 | specified by the pid stored in the file signifies the process death and | ||
61 | triggers restart. | ||
62 | .TP | ||
63 | \fBRestartCommand\fR \fIcommand\fR | ||
64 | Specifies the command to run in order to restart the process. | ||
65 | .TP | ||
66 | \fBUser\fR \fIuser\fR | ||
67 | Specifies the unix user to change to before executing the command | ||
68 | specified by \fBRestartCommand\fR. | ||
69 | .TP | ||
70 | \fBGroup\fR \fIgroup\fR | ||
71 | Specifies the unix group to change to before executing the command | ||
72 | specified by \fBRestartCommand\fR. | ||
73 | .TP | ||
74 | \fBChroot\fR \fIpath\fR | ||
75 | Specifies the directory to set filesystem root to before executing the | ||
76 | command specified by by \fBRestartCommand\fR. | ||
77 | .TP | ||
78 | \fBMailtoHeader\fR \fIheader\fR \fIcontent\fR | ||
79 | Specifies extra headers to add to mailto: notifications sent by the | ||
80 | dudki daemon. Headers specified in per-process section override the | ||
81 | ones specified globally.A | ||
82 | .TP | ||
83 | \fBNotify\fR \fIschema\fR:\fItarget\fR | ||
84 | Specifies the contact to be notified whenever something notable happens | ||
85 | to the process. The only schema currently supported is 'mailto:'. | ||
86 | .TP | ||
87 | \fB</Process>\fR | ||
88 | Signifies the end of process section. | ||
89 | |||
90 | .SH EXAMPLE | ||
91 | |||
92 | .br | ||
93 | CheckInterval 60 | ||
94 | .br | ||
95 | PidFile /var/run/dudki.pid | ||
96 | .br | ||
97 | Daemonize on | ||
98 | .br | ||
99 | MailtoHeader From "Dudki <root@klever.net>" | ||
100 | .br | ||
101 | MailtoHeader Reply-To devnull@klever.net | ||
102 | .br | ||
103 | Notify mailto:hacker@klever.net | ||
104 | .br | ||
105 | |||
106 | .br | ||
107 | <Process apache> | ||
108 | .br | ||
109 | PidFile /var/run/httpd.pid | ||
110 | .br | ||
111 | RestartCommand "exec /usr/sbin/apachectl start" | ||
112 | .br | ||
113 | </Process> | ||
114 | .br | ||
115 | <Process named> | ||
116 | .br | ||
117 | PidFile /var/run/named.pid | ||
118 | .br | ||
119 | RestartCommand "exec /usr/sbin/named" | ||
120 | .br | ||
121 | </Process> | ||
122 | |||
123 | .SH AUTHOR | ||
124 | |||
125 | Written by Michael 'hacker' Krelin <hacker@klever.net> | ||
126 | |||
127 | .SH COPYRIGHT | ||
128 | |||
129 | Copyright (c) 2004 Klever Group (http://www.klever.net/) | ||
130 | |||
131 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
132 | this software and associated documentation files (the "Software"), to deal in | ||
133 | the Software without restriction, including without limitation the rights to | ||
134 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
135 | of the Software, and to permit persons to whom the Software is furnished to do | ||
136 | so, subject to the following conditions: | ||
137 | |||
138 | The above copyright notice and this permission notice shall be included in all | ||
139 | copies or substantial portions of the Software. | ||
140 | |||
141 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
142 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
143 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
144 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
145 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
146 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
147 | SOFTWARE. | ||
148 | |||
149 | .SH BUGS | ||
150 | |||
151 | You tell me. Send reports to <dudki-bugs@klever.net> | ||
152 | |||
153 | .SH SEE ALSO | ||
154 | \fBdudki\fR(8), \fBinit\fR(8), \fBinittab\fR(5) | ||
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..e26d996 --- a/dev/null +++ b/src/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | .deps | ||
2 | Makefile | ||
3 | Makefile.in | ||
4 | COPYING.cc | ||
5 | dudki | ||
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..3810272 --- a/dev/null +++ b/src/Makefile.am | |||
@@ -0,0 +1,18 @@ | |||
1 | sbin_PROGRAMS = dudki | ||
2 | |||
3 | INCLUDES = ${DOTCONF_CFLAGS} | ||
4 | LIBS += ${DOTCONF_LIBS} | ||
5 | AM_CPPFLAGS = \ | ||
6 | -DDEFAULT_CONF_FILE=\"${sysconfdir}/${PACKAGE}.conf\" \ | ||
7 | -DDEFAULT_PID_FILE=\"/var/run/${PACKAGE}.pid\" | ||
8 | |||
9 | dudki_SOURCES = dudki.cc \ | ||
10 | process.cc process.h \ | ||
11 | configuration.cc configuration.h \ | ||
12 | util.cc util.h \ | ||
13 | COPYING.cc | ||
14 | |||
15 | COPYING.cc: ${top_srcdir}/COPYING | ||
16 | echo "const char * COPYING =" >$@ || (rm $@;exit 1) | ||
17 | sed 's/"/\\"/g' $< | sed 's/^/\"/' | sed 's/$$/\\n\"/' >>$@ || (rm $@;exit 1) | ||
18 | echo ";" >>$@ || (rm $@;exit 1) | ||
diff --git a/src/configuration.cc b/src/configuration.cc new file mode 100644 index 0000000..eb010c1 --- a/dev/null +++ b/src/configuration.cc | |||
@@ -0,0 +1,149 @@ | |||
1 | #include <stdexcept> | ||
2 | using namespace std; | ||
3 | #include <dotconf.h> | ||
4 | #include "configuration.h" | ||
5 | |||
6 | #ifndef DEFAULT_PID_FILE | ||
7 | # define DEFAULT_PID_FILE "/var/run/dudki.pid" | ||
8 | #endif | ||
9 | |||
10 | configuration::configuration() | ||
11 | : check_interval(60), pidfile(DEFAULT_PID_FILE), | ||
12 | daemonize(true) { | ||
13 | } | ||
14 | |||
15 | enum dc_ctx { | ||
16 | DCC_ROOT = 1, | ||
17 | DCC_PROCESS = 2 | ||
18 | }; | ||
19 | struct dc_context { | ||
20 | dc_ctx ctx; | ||
21 | configuration* cf; | ||
22 | process* ps; | ||
23 | |||
24 | dc_context() | ||
25 | : ctx(DCC_ROOT), cf(NULL), ps(NULL) { } | ||
26 | }; | ||
27 | |||
28 | static DOTCONF_CB(dco_check_interval) { dc_context *dcc = (dc_context*)ctx; | ||
29 | dcc->cf->check_interval = cmd->data.value; | ||
30 | return NULL; | ||
31 | } | ||
32 | static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*)ctx; | ||
33 | dcc->cf->daemonize = cmd->data.value; | ||
34 | return NULL; | ||
35 | } | ||
36 | |||
37 | static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx; | ||
38 | switch(dcc->ctx) { | ||
39 | case DCC_ROOT: | ||
40 | dcc->cf->pidfile = cmd->data.str; | ||
41 | break; | ||
42 | case DCC_PROCESS: | ||
43 | dcc->ps->pidfile = cmd->data.str; | ||
44 | break; | ||
45 | default: | ||
46 | return "Unexpected PidFile"; | ||
47 | } | ||
48 | return NULL; | ||
49 | } | ||
50 | static DOTCONF_CB(dco_mailto_header) { dc_context *dcc = (dc_context*)ctx; | ||
51 | if(cmd->arg_count!=2) | ||
52 | return "Invalid number of arguments"; | ||
53 | string h = cmd->data.list[0]; | ||
54 | string v = cmd->data.list[1]; | ||
55 | switch(dcc->ctx) { | ||
56 | case DCC_ROOT: | ||
57 | dcc->cf->mailto_headers[h] = v; | ||
58 | break; | ||
59 | case DCC_PROCESS: | ||
60 | dcc->ps->mailto_headers[h] = v; | ||
61 | break; | ||
62 | default: | ||
63 | return "Unexpected MailtoHeader"; | ||
64 | } | ||
65 | return NULL; | ||
66 | } | ||
67 | static DOTCONF_CB(dco_notify) { dc_context *dcc = (dc_context*)ctx; | ||
68 | switch(dcc->ctx) { | ||
69 | case DCC_ROOT: | ||
70 | dcc->cf->notify = cmd->data.str; | ||
71 | break; | ||
72 | case DCC_PROCESS: | ||
73 | dcc->ps->notify = cmd->data.str; | ||
74 | break; | ||
75 | default: | ||
76 | return "Unexpected Notify"; | ||
77 | } | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | static DOTCONF_CB(dco_process) { dc_context *dcc = (dc_context*)ctx; | ||
82 | string id = cmd->data.str; | ||
83 | if(id[id.length()-1]=='>') | ||
84 | id.erase(id.length()-1); | ||
85 | dcc->ps = &(dcc->cf->processes[id]); | ||
86 | dcc->ctx = DCC_PROCESS; | ||
87 | return NULL; | ||
88 | } | ||
89 | static DOTCONF_CB(dco__process) { dc_context *dcc = (dc_context*)ctx; | ||
90 | dcc->ps = NULL; | ||
91 | dcc->ctx = DCC_ROOT; | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | static DOTCONF_CB(dco_restart_command) { dc_context *dcc = (dc_context*)ctx; | ||
96 | dcc->ps->restart_cmd = cmd->data.str; | ||
97 | return NULL; | ||
98 | } | ||
99 | static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx; | ||
100 | dcc->ps->user = cmd->data.str; | ||
101 | return NULL; | ||
102 | } | ||
103 | static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx; | ||
104 | dcc->ps->group = cmd->data.str; | ||
105 | return NULL; | ||
106 | } | ||
107 | static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx; | ||
108 | dcc->ps->chroot = cmd->data.str; | ||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static const configoption_t dc_options[] = { | ||
113 | { "CheckInterval", ARG_INT, dco_check_interval, NULL, DCC_ROOT }, | ||
114 | { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT }, | ||
115 | { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT|DCC_PROCESS }, | ||
116 | { "MailtoHeader", ARG_STR, dco_mailto_header, NULL, DCC_ROOT|DCC_PROCESS }, | ||
117 | { "Notify", ARG_STR, dco_notify, NULL, DCC_ROOT|DCC_PROCESS }, | ||
118 | { "<Process", ARG_STR, dco_process, NULL, DCC_ROOT }, | ||
119 | { "RestartCommand", ARG_STR, dco_restart_command, NULL, DCC_PROCESS }, | ||
120 | { "User", ARG_STR, dco_user, NULL, DCC_PROCESS }, | ||
121 | { "Group", ARG_STR, dco_group, NULL, DCC_PROCESS }, | ||
122 | { "Chroot", ARG_STR, dco_chroot, NULL, DCC_PROCESS }, | ||
123 | { "</Process>", ARG_NONE, dco__process, NULL, DCC_PROCESS }, | ||
124 | LAST_OPTION | ||
125 | }; | ||
126 | |||
127 | static const char *dc_context_checker(command_t *cmd,unsigned long mask) { | ||
128 | dc_context *dcc = (dc_context*)cmd->context; | ||
129 | if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) ) | ||
130 | return NULL; | ||
131 | return "misplaced option"; | ||
132 | } | ||
133 | static FUNC_ERRORHANDLER(dc_error_handler) { | ||
134 | throw runtime_error(string("error parsing config file: ")+msg); | ||
135 | } | ||
136 | |||
137 | void configuration::parse(const string& cfile) { | ||
138 | struct dc_context dcc; | ||
139 | dcc.cf = this; | ||
140 | dcc.ctx = DCC_ROOT; | ||
141 | configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE); | ||
142 | if(!cf) | ||
143 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_create()"); | ||
144 | cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler; | ||
145 | cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker; | ||
146 | if(!dotconf_command_loop(cf)) | ||
147 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to dotconf_command_loop()"); | ||
148 | dotconf_cleanup(cf); | ||
149 | } | ||
diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000..314af92 --- a/dev/null +++ b/src/configuration.h | |||
@@ -0,0 +1,23 @@ | |||
1 | #ifndef __CONFIGURATION_H | ||
2 | #define __CONFIGURATION_H | ||
3 | |||
4 | #include <string> | ||
5 | using namespace std; | ||
6 | #include "process.h" | ||
7 | |||
8 | class configuration { | ||
9 | public: | ||
10 | processes_t processes; | ||
11 | |||
12 | int check_interval; | ||
13 | string pidfile; | ||
14 | bool daemonize; | ||
15 | headers_t mailto_headers; | ||
16 | string notify; | ||
17 | |||
18 | configuration(); | ||
19 | |||
20 | void parse(const string& cfile); | ||
21 | }; | ||
22 | |||
23 | #endif /* __CONFIGURATION_H */ | ||
diff --git a/src/dudki.cc b/src/dudki.cc new file mode 100644 index 0000000..3c50e56 --- a/dev/null +++ b/src/dudki.cc | |||
@@ -0,0 +1,244 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <signal.h> | ||
3 | #include <syslog.h> | ||
4 | #include <iostream> | ||
5 | #include <fstream> | ||
6 | #include <stdexcept> | ||
7 | using namespace std; | ||
8 | #include "configuration.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | #include "config.h" | ||
12 | #ifdef HAVE_GETOPT_H | ||
13 | # include <getopt.h> | ||
14 | #endif | ||
15 | |||
16 | #ifndef DEFAULT_CONF_FILE | ||
17 | # define DEFAULT_CONF_FILE "/etc/dudki.conf" | ||
18 | #endif | ||
19 | |||
20 | #define PHEADER PACKAGE " Version " VERSION | ||
21 | #define PCOPY "Copyright (c) 2004 Klever Group" | ||
22 | |||
23 | bool finishing = false; | ||
24 | static char **_argv = NULL; | ||
25 | |||
26 | static void lethal_signal_handler(int signum) { | ||
27 | syslog(LOG_NOTICE,"Lethal signal received. Terminating."); | ||
28 | finishing = true; | ||
29 | } | ||
30 | static void sighup_handler(int signum) { | ||
31 | syslog(LOG_NOTICE,"SUGHUP received, reloading."); | ||
32 | execvp(_argv[0],_argv); | ||
33 | } | ||
34 | |||
35 | void check_herd(configuration& config) { | ||
36 | for(processes_t::iterator i=config.processes.begin();i!=config.processes.end();++i) | ||
37 | i->second.check(i->first,config); | ||
38 | } | ||
39 | |||
40 | void signal_self(const configuration& config,int signum) { | ||
41 | ifstream pids(config.pidfile.c_str(),ios::in); | ||
42 | if(!pids) | ||
43 | throw runtime_error("Can't detect running instance"); | ||
44 | pid_t pid = 0; | ||
45 | pids >> pid; | ||
46 | if(!pid) | ||
47 | throw runtime_error("Can't detect running instance"); | ||
48 | if(pid==getpid()) | ||
49 | throw 0; | ||
50 | if(kill(pid,signum)) | ||
51 | throw runtime_error("Failed to signal running instance"); | ||
52 | } | ||
53 | |||
54 | int main(int argc,char **argv) { | ||
55 | try { | ||
56 | _argv = new char*[argc+1]; | ||
57 | if(!_argv) | ||
58 | throw runtime_error("memory allocation problem at the very start"); | ||
59 | memmove(_argv,argv,sizeof(*_argv)*(argc+1)); | ||
60 | string config_file = DEFAULT_CONF_FILE; | ||
61 | enum { | ||
62 | op_default, | ||
63 | op_work, | ||
64 | op_hup, | ||
65 | op_term, | ||
66 | op_check, | ||
67 | op_ensure, | ||
68 | op_test | ||
69 | } op = op_default; | ||
70 | while(true) { | ||
71 | #defineSHORTOPTSTRING "f:hVLrkcet" | ||
72 | #ifdef HAVE_GETOPT_LONG | ||
73 | static struct option opts[] = { | ||
74 | { "help", no_argument, 0, 'h' }, | ||
75 | { "usage", no_argument, 0, 'h' }, | ||
76 | { "version", no_argument, 0, 'V' }, | ||
77 | { "license", no_argument, 0, 'L' }, | ||
78 | { "config", required_argument, 0, 'f' }, | ||
79 | { "kill", no_argument, 0, 'k' }, | ||
80 | { "reload", no_argument, 0, 'r' }, | ||
81 | { "check", no_argument, 0, 'c' }, | ||
82 | { "ensure", no_argument, 0, 'e' }, | ||
83 | { "test", no_argument, 0, 't' }, | ||
84 | { NULL, 0, 0, 0 } | ||
85 | }; | ||
86 | int c = getopt_long(argc,argv,SHORTOPTSTRING,opts,NULL); | ||
87 | #else /* !HAVE_GETOPT_LONG */ | ||
88 | int c = getopt(argc,argv,SHORTOPTSTRING); | ||
89 | #endif /* /HAVE_GETOPT_LONG */ | ||
90 | if(c==-1) | ||
91 | break; | ||
92 | switch(c) { | ||
93 | case 'h': | ||
94 | cerr << PHEADER << endl | ||
95 | << PCOPY << endl << endl << | ||
96 | #ifdef HAVE_GETOPT_LONG | ||
97 | " -h, --help\n" | ||
98 | " --usage display this text\n" | ||
99 | " -V, --version display version number\n" | ||
100 | " -L, --license show license\n" | ||
101 | " -f filename, --config=filename\n" | ||
102 | " specify the configuration file to use\n" | ||
103 | "\n" | ||
104 | " -k, --kill stop running instance\n" | ||
105 | " -r, --reload reload running instance (send SIGHUP)\n" | ||
106 | " -c, --check check if dudki is running\n" | ||
107 | " -e, --ensure ensure that dudki is running\n" | ||
108 | " -t, --test test configuration file and exit" | ||
109 | #else /* !HAVE_GETOPT_LONG */ | ||
110 | " -h display this text\n" | ||
111 | " -V display version number\n" | ||
112 | " -L show license\n" | ||
113 | " -f filename specify the configuration file to use\n" | ||
114 | "\n" | ||
115 | " -k stop running instance\n" | ||
116 | " -r reload running instance (send SIGHUP)\n" | ||
117 | " -c check if dudki is running\n" | ||
118 | " -e ensure that dudki is running\n" | ||
119 | " -t test configuration file and exit" | ||
120 | #endif /* /HAVE_GETOPT_LONG */ | ||
121 | << endl; | ||
122 | exit(0); | ||
123 | break; | ||
124 | case 'V': | ||
125 | cerr << VERSION << endl; | ||
126 | exit(0); | ||
127 | break; | ||
128 | case 'L': | ||
129 | extern const char *COPYING; | ||
130 | cerr << COPYING << endl; | ||
131 | exit(0); | ||
132 | break; | ||
133 | case 'f': | ||
134 | config_file = optarg; | ||
135 | break; | ||
136 | case 'k': | ||
137 | if(op!=op_default) { | ||
138 | cerr << "Can't obey two or more orders at once" << endl; | ||
139 | exit(1); | ||
140 | } | ||
141 | op = op_term; | ||
142 | break; | ||
143 | case 'r': | ||
144 | if(op!=op_default) { | ||
145 | cerr << "Can't obey two or more orders at once" << endl; | ||
146 | exit(1); | ||
147 | } | ||
148 | op = op_hup; | ||
149 | break; | ||
150 | case 'c': | ||
151 | if(op!=op_default) { | ||
152 | cerr << "Can't obey two or more orders at once" << endl; | ||
153 | exit(1); | ||
154 | } | ||
155 | op = op_check; | ||
156 | break; | ||
157 | case 'e': | ||
158 | if(op!=op_default) { | ||
159 | cerr << "Can't obey two or more orders at once" << endl; | ||
160 | exit(1); | ||
161 | } | ||
162 | op = op_ensure; | ||
163 | break; | ||
164 | case 't': | ||
165 | if(op!=op_default) { | ||
166 | cerr << "Can't obey two or more orders at once" << endl; | ||
167 | exit(1); | ||
168 | } | ||
169 | op = op_test; | ||
170 | break; | ||
171 | default: | ||
172 | cerr << "Huh??" << endl; | ||
173 | exit(1); | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | const char *sid = *argv; | ||
178 | const char *t; | ||
179 | while(t = index(sid,'/')) { | ||
180 | sid = t; sid++; | ||
181 | } | ||
182 | openlog(sid,LOG_CONS|LOG_PERROR|LOG_PID,LOG_DAEMON); | ||
183 | configuration config; | ||
184 | config.parse(config_file); | ||
185 | switch(op) { | ||
186 | case op_test: | ||
187 | cerr << "Configuration OK" << endl; | ||
188 | break; | ||
189 | case op_hup: | ||
190 | signal_self(config,SIGHUP); | ||
191 | break; | ||
192 | case op_term: | ||
193 | signal_self(config,SIGTERM); | ||
194 | break; | ||
195 | case op_check: | ||
196 | try{ | ||
197 | signal_self(config,0); | ||
198 | exit(0); | ||
199 | }catch(exception& e) { | ||
200 | exit(1); | ||
201 | } | ||
202 | case op_ensure: | ||
203 | try { | ||
204 | signal_self(config,0); | ||
205 | break; | ||
206 | }catch(exception& e) { | ||
207 | syslog(LOG_NOTICE,"The dudki process is down, taking its place"); | ||
208 | config.daemonize = true; | ||
209 | }catch(int zero) { | ||
210 | // we throw zero in case we're ensuring that this very process is running. | ||
211 | // we don't have to daemonize if we're daemonic. | ||
212 | config.daemonize = false; | ||
213 | } | ||
214 | case op_default: | ||
215 | case op_work: | ||
216 | { | ||
217 | if(config.daemonize) { | ||
218 | pid_t pf = fork(); | ||
219 | if(pf<0) | ||
220 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); | ||
221 | if(pf) { | ||
222 | _exit(0); | ||
223 | } | ||
224 | } | ||
225 | pid_file pidfile; | ||
226 | pidfile.set(config.pidfile); | ||
227 | signal(SIGINT,lethal_signal_handler); | ||
228 | signal(SIGABRT,lethal_signal_handler); | ||
229 | signal(SIGTERM,lethal_signal_handler); | ||
230 | signal(SIGHUP,sighup_handler); | ||
231 | while(!finishing) { | ||
232 | check_herd(config); | ||
233 | sleep(config.check_interval); | ||
234 | } | ||
235 | } | ||
236 | break; | ||
237 | default: | ||
238 | throw runtime_error(string(__PRETTY_FUNCTION__)+": internal error"); | ||
239 | } | ||
240 | }catch(exception& e) { | ||
241 | cerr << "Oops: " << e.what() << endl; | ||
242 | return 1; | ||
243 | } | ||
244 | } | ||
diff --git a/src/process.cc b/src/process.cc new file mode 100644 index 0000000..fda35e8 --- a/dev/null +++ b/src/process.cc | |||
@@ -0,0 +1,184 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <unistd.h> | ||
4 | #include <signal.h> | ||
5 | #include <pwd.h> | ||
6 | #include <grp.h> | ||
7 | #include <sys/wait.h> | ||
8 | #include <syslog.h> | ||
9 | #include <errno.h> | ||
10 | #include <iostream> | ||
11 | #include <fstream> | ||
12 | #include <stdexcept> | ||
13 | using namespace std; | ||
14 | #include "process.h" | ||
15 | #include "configuration.h" | ||
16 | |||
17 | void process::check(const string& id,configuration& config) { | ||
18 | bool running = false; | ||
19 | ifstream pids(pidfile.c_str(),ios::in); | ||
20 | if(pids) { | ||
21 | pid_t pid = 0; | ||
22 | pids >> pid; | ||
23 | pids.close(); | ||
24 | if(pid) { | ||
25 | if(!kill(pid,0)) { | ||
26 | running = true; | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | if(running){ | ||
31 | patience = 0; | ||
32 | }else{ | ||
33 | if(patience>60) { // TODO: configurable | ||
34 | patience = 0; | ||
35 | }else{ | ||
36 | if(patience<10) { // TODO: configurable | ||
37 | syslog(LOG_NOTICE,"The process '%s' is down, trying to launch.",id.c_str()); | ||
38 | do_notify(id,"Starting up", | ||
39 | "The named process seems to be down. Dudki will try\n" | ||
40 | "to revive it by running the specified command.\n", | ||
41 | config); | ||
42 | try { | ||
43 | launch(id,config); | ||
44 | }catch(exception& e) { | ||
45 | syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); | ||
46 | } | ||
47 | }else if(patience==10){ // TODO: configurable like the above | ||
48 | syslog(LOG_NOTICE,"Giving up on process '%s' for a while",id.c_str()); | ||
49 | do_notify(id,"Giving up", | ||
50 | "After a number of attempts to relaunch the named process\n" | ||
51 | "It still seems to be down. Dudki is giving up attempts\n" | ||
52 | "to revive the process for a while.\n", | ||
53 | config); | ||
54 | } | ||
55 | patience++; | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | void process::launch(const string& id,configuration& config) { | ||
61 | uid_t uid = 0; | ||
62 | if(!user.empty()) { | ||
63 | struct passwd *ptmp = getpwnam(user.c_str()); | ||
64 | if(ptmp) { | ||
65 | uid = ptmp->pw_uid; | ||
66 | }else{ | ||
67 | errno=0; | ||
68 | uid = strtol(user.c_str(),NULL,0); | ||
69 | if(errno) | ||
70 | throw runtime_error("Failed to resolve User value to uid"); | ||
71 | } | ||
72 | } | ||
73 | gid_t gid = 0; | ||
74 | if(!group.empty()) { | ||
75 | struct group *gtmp = getgrnam(group.c_str()); | ||
76 | if(gtmp) { | ||
77 | gid = gtmp->gr_gid; | ||
78 | }else{ | ||
79 | errno = 0; | ||
80 | gid = strtol(group.c_str(),NULL,0); | ||
81 | if(errno) | ||
82 | throw runtime_error("Failed to reslove Group value to gid"); | ||
83 | } | ||
84 | } | ||
85 | pid_t p = fork(); | ||
86 | if(p<0) | ||
87 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to fork()"); | ||
88 | if(!p) { | ||
89 | // child | ||
90 | try { | ||
91 | setsid(); | ||
92 | if(!chroot.empty()) { | ||
93 | if(::chroot(chroot.c_str())) | ||
94 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to chroot()"); | ||
95 | } | ||
96 | if(!group.empty()) { | ||
97 | // TODO: initgroups()? | ||
98 | if((getgid()!=gid) && setgid(gid)) | ||
99 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setgid()"); | ||
100 | } | ||
101 | if(!user.empty()) { | ||
102 | if((getuid()!=uid) && setuid(uid)) | ||
103 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to setuid()"); | ||
104 | } | ||
105 | char *argv[] = { "/bin/sh", "-c", (char*)restart_cmd.c_str(), NULL }; | ||
106 | close(0); close(1); close(2); | ||
107 | execv("/bin/sh",argv); | ||
108 | }catch(exception& e) { | ||
109 | syslog(LOG_ERR,"Error trying to launch process '%s': %s",id.c_str(),e.what()); | ||
110 | } | ||
111 | _exit(-1); | ||
112 | } | ||
113 | // parent | ||
114 | int rv; | ||
115 | if(waitpid(p,&rv,0)<0) | ||
116 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to waitpid()"); | ||
117 | } | ||
118 | |||
119 | void process::do_notify(const string& id,const string& event,const string& description,configuration& config) { | ||
120 | string the_notify; | ||
121 | if(!notify.empty()) | ||
122 | the_notify=notify; | ||
123 | else if(!config.notify.empty()) | ||
124 | the_notify=config.notify; | ||
125 | else | ||
126 | return; | ||
127 | try { | ||
128 | string::size_type colon = the_notify.find(':'); | ||
129 | if(colon==string::npos) | ||
130 | throw runtime_error("invalid notify action specification"); | ||
131 | string nschema = the_notify.substr(0,colon); | ||
132 | string ntarget = the_notify.substr(colon+1); | ||
133 | if(nschema=="mailto") { | ||
134 | notify_mailto(ntarget,id,event,description,config); | ||
135 | }else | ||
136 | throw runtime_error("unrecognized notification schema"); | ||
137 | }catch(exception& e) { | ||
138 | syslog(LOG_ERR,"Notification error: %s",e.what()); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | void process::notify_mailto(const string& email,const string& id,const string& event,const string& description,configuration& config) { | ||
143 | int files[2]; | ||
144 | if(pipe(files)) | ||
145 | throw runtime_error("Failed to pipe()"); | ||
146 | pid_t pid = vfork(); | ||
147 | if(pid==-1) { | ||
148 | close(files[0]); | ||
149 | close(files[1]); | ||
150 | throw runtime_error("Failed to vfork()"); | ||
151 | } | ||
152 | if(!pid) { | ||
153 | // child | ||
154 | if(dup2(files[0],0)!=0) | ||
155 | _exit(-1); | ||
156 | close(1); | ||
157 | close(files[0]); | ||
158 | close(files[1]); | ||
159 | execl("/usr/sbin/sendmail","usr/sbin/sendmail","-i",email.c_str(),NULL); | ||
160 | _exit(-1); | ||
161 | } | ||
162 | // parent | ||
163 | close(files[0]); | ||
164 | FILE *mta = fdopen(files[1],"w"); | ||
165 | for(headers_t::const_iterator i=mailto_headers.begin();i!=mailto_headers.end();++i) { | ||
166 | fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); | ||
167 | } | ||
168 | for(headers_t::const_iterator i=config.mailto_headers.begin();i!=config.mailto_headers.end();++i) { | ||
169 | if(mailto_headers.find(i->first)!=mailto_headers.end()) | ||
170 | continue; | ||
171 | fprintf(mta,"%s: %s\n",i->first.c_str(),i->second.c_str()); | ||
172 | } | ||
173 | fprintf(mta, | ||
174 | "Subject: [%s] %s\n\n" | ||
175 | "%s\n" | ||
176 | "---\n" | ||
177 | "This message was sent automatically by the 'dudki' daemon\n", | ||
178 | id.c_str(), event.c_str(), | ||
179 | description.c_str() ); | ||
180 | fclose(mta); | ||
181 | int status; | ||
182 | waitpid(pid,&status,0); | ||
183 | // TODO: check the return code | ||
184 | } | ||
diff --git a/src/process.h b/src/process.h new file mode 100644 index 0000000..b6d7091 --- a/dev/null +++ b/src/process.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef __PROCESS_H | ||
2 | #define __PROCESS_H | ||
3 | |||
4 | #include <string> | ||
5 | #include <map> | ||
6 | using namespace std; | ||
7 | |||
8 | class configuration; | ||
9 | |||
10 | typedef map<string,string> headers_t; | ||
11 | |||
12 | class process { | ||
13 | public: | ||
14 | string pidfile; | ||
15 | string restart_cmd; | ||
16 | string notify; | ||
17 | string user; | ||
18 | string group; | ||
19 | string chroot; | ||
20 | headers_t mailto_headers; | ||
21 | |||
22 | int patience; | ||
23 | |||
24 | process() | ||
25 | : patience(0) { } | ||
26 | |||
27 | void check(const string& id,configuration& config); | ||
28 | void launch(const string& id,configuration& config); | ||
29 | 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 | const string& description,configuration& config); | ||
32 | }; | ||
33 | |||
34 | typedef map<string,process> processes_t; | ||
35 | |||
36 | #endif /* __PROCESS_H */ | ||
diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..afb2641 --- a/dev/null +++ b/src/util.cc | |||
@@ -0,0 +1,21 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <fstream> | ||
3 | #include <stdexcept> | ||
4 | using namespace std; | ||
5 | #include "util.h" | ||
6 | |||
7 | void pid_file::set(const string& f,bool u) { | ||
8 | ofstream of(f.c_str(),ios::trunc); | ||
9 | if(!of) | ||
10 | throw runtime_error(string(__PRETTY_FUNCTION__)+": failed to open file for writing pid"); | ||
11 | of << getpid() << endl; | ||
12 | of.close(); | ||
13 | file_name = f; | ||
14 | unlink_pid = u; | ||
15 | } | ||
16 | void pid_file::unlink() { | ||
17 | if(!unlink_pid) | ||
18 | return; | ||
19 | ::unlink(file_name.c_str()); | ||
20 | } | ||
21 | |||
diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..314d8e2 --- a/dev/null +++ b/src/util.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef __UTIL_H | ||
2 | #define __UTIL_H | ||
3 | |||
4 | #include <string> | ||
5 | using namespace std; | ||
6 | |||
7 | class pid_file { | ||
8 | public: | ||
9 | string file_name; | ||
10 | bool unlink_pid; | ||
11 | |||
12 | pid_file() | ||
13 | : unlink_pid(false) { } | ||
14 | ~pid_file() { unlink(); } | ||
15 | |||
16 | void set(const string& f,bool u=true); | ||
17 | void unlink(); | ||
18 | }; | ||
19 | |||
20 | #endif /* __UTIL_H */ | ||