1 /**
2 * collectd - src/collectdmon.c
3 * Copyright (C) 2007 Sebastian Harl
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author:
19 * Sebastian Harl <sh at tokkee.org>
20 **/
22 #include "config.h"
24 #include <assert.h>
26 #include <errno.h>
28 #include <fcntl.h>
30 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
35 #include <string.h>
37 #include <syslog.h>
39 #include <sys/resource.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
45 #include <time.h>
47 #include <unistd.h>
49 #ifndef COLLECTDMON_PIDFILE
50 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
51 #endif /* ! COLLECTDMON_PIDFILE */
53 #ifndef WCOREDUMP
54 # define WCOREDUMP(s) 0
55 #endif /* ! WCOREDUMP */
57 static int loop = 0;
58 static int restart = 0;
60 static char *pidfile = NULL;
61 static pid_t collectd_pid = 0;
63 static void exit_usage (char *name)
64 {
65 printf ("Usage: %s <options> [-- <collectd options>]\n"
67 "\nAvailable options:\n"
68 " -h Display this help and exit.\n"
69 " -c <path> Path to the collectd binary.\n"
70 " -P <file> PID-file.\n"
72 "\nFor <collectd options> see collectd.conf(5).\n"
74 "\n"PACKAGE" "VERSION", http://collectd.org/\n"
75 "by Florian octo Forster <octo@verplant.org>\n"
76 "for contributions see `AUTHORS'\n", name);
77 exit (0);
78 } /* exit_usage */
80 static int pidfile_create (void)
81 {
82 FILE *file = NULL;
84 if (NULL == pidfile)
85 pidfile = COLLECTDMON_PIDFILE;
87 if (NULL == (file = fopen (pidfile, "w"))) {
88 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
89 pidfile, strerror (errno));
90 return -1;
91 }
93 fprintf (file, "%i\n", (int)getpid ());
94 fclose (file);
95 return 0;
96 } /* pidfile_create */
98 static int pidfile_delete (void)
99 {
100 assert (NULL != pidfile);
102 if (0 != unlink (pidfile)) {
103 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
104 pidfile, strerror (errno));
105 return -1;
106 }
107 return 0;
108 } /* pidfile_remove */
110 static int daemonize (void)
111 {
112 struct rlimit rl;
114 pid_t pid = 0;
115 int i = 0;
117 if (0 != chdir ("/")) {
118 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
119 return -1;
120 }
122 if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
123 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
124 return -1;
125 }
127 if (0 > (pid = fork ())) {
128 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
129 return -1;
130 }
131 else if (pid != 0) {
132 exit (0);
133 }
135 if (0 != pidfile_create ())
136 return -1;
138 setsid ();
140 if (RLIM_INFINITY == rl.rlim_max)
141 rl.rlim_max = 1024;
143 for (i = 0; i < rl.rlim_max; ++i)
144 close (i);
146 errno = 0;
147 if (open ("/dev/null", O_RDWR) != 0) {
148 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
149 strerror (errno));
150 return -1;
151 }
153 errno = 0;
154 if (dup (0) != 1) {
155 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
156 strerror (errno));
157 return -1;
158 }
160 errno = 0;
161 if (dup (0) != 2) {
162 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
163 strerror (errno));
164 return -1;
165 }
166 return 0;
167 } /* daemonize */
169 static int collectd_start (int argc, char **argv)
170 {
171 pid_t pid = 0;
173 if (0 > (pid = fork ())) {
174 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
175 return -1;
176 }
177 else if (pid != 0) {
178 collectd_pid = pid;
179 return 0;
180 }
182 execvp (argv[0], argv);
183 syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
184 argv[0], strerror (errno));
185 exit (-1);
186 } /* collectd_start */
188 static int collectd_stop (void)
189 {
190 if (0 == collectd_pid)
191 return 0;
193 if (0 != kill (collectd_pid, SIGTERM)) {
194 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
195 return -1;
196 }
197 return 0;
198 } /* collectd_stop */
200 static void sig_int_term_handler (int signo)
201 {
202 ++loop;
203 return;
204 } /* sig_int_term_handler */
206 static void sig_hup_handler (int signo)
207 {
208 ++restart;
209 return;
210 } /* sig_hup_handler */
212 static void log_status (int status)
213 {
214 if (WIFEXITED (status)) {
215 if (0 == WEXITSTATUS (status))
216 syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
217 WEXITSTATUS (status));
218 else
219 syslog (LOG_WARNING,
220 "Warning: collectd terminated with exit status %i",
221 WEXITSTATUS (status));
222 }
223 else if (WIFSIGNALED (status)) {
224 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
225 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
226 }
227 return;
228 } /* log_status */
230 static void check_respawn (void)
231 {
232 time_t t = time (NULL);
234 static time_t timestamp = 0;
235 static int counter = 0;
237 if ((t - 120) < timestamp)
238 ++counter;
239 else {
240 timestamp = t;
241 counter = 0;
242 }
244 if (10 < counter) {
245 unsigned int time_left = 300;
247 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
248 "disabled for %i seconds", time_left);
250 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
251 }
252 return;
253 } /* check_respawn */
255 int main (int argc, char **argv)
256 {
257 int collectd_argc = 0;
258 char *collectd = NULL;
259 char **collectd_argv = NULL;
261 struct sigaction sa;
263 int i = 0;
265 /* parse command line options */
266 while (42) {
267 int c = getopt (argc, argv, "hc:P:");
269 if (-1 == c)
270 break;
272 switch (c) {
273 case 'c':
274 collectd = optarg;
275 break;
276 case 'P':
277 pidfile = optarg;
278 break;
279 case 'h':
280 default:
281 exit_usage (argv[0]);
282 }
283 }
285 for (i = optind; i < argc; ++i)
286 if (0 == strcmp (argv[i], "-f"))
287 break;
289 /* i < argc => -f already present */
290 collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
291 collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
293 if (NULL == collectd_argv) {
294 fprintf (stderr, "Out of memory.");
295 return 3;
296 }
298 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
300 if (i == argc)
301 collectd_argv[collectd_argc - 1] = "-f";
303 for (i = optind; i < argc; ++i)
304 collectd_argv[i - optind + 1] = argv[i];
306 collectd_argv[collectd_argc] = NULL;
308 openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
310 if (-1 == daemonize ())
311 return 1;
313 sa.sa_handler = sig_int_term_handler;
314 sa.sa_flags = 0;
315 sigemptyset (&sa.sa_mask);
317 if (0 != sigaction (SIGINT, &sa, NULL)) {
318 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
319 return 1;
320 }
322 if (0 != sigaction (SIGTERM, &sa, NULL)) {
323 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
324 return 1;
325 }
327 sa.sa_handler = sig_hup_handler;
329 if (0 != sigaction (SIGHUP, &sa, NULL)) {
330 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
331 return 1;
332 }
334 sigaddset (&sa.sa_mask, SIGCHLD);
335 if (0 != sigprocmask (SIG_BLOCK, &sa.sa_mask, NULL)) {
336 syslog (LOG_ERR, "Error: sigprocmask() failed: %s", strerror (errno));
337 return 1;
338 }
340 while (0 == loop) {
341 int status = 0;
343 if (0 != collectd_start (collectd_argc, collectd_argv)) {
344 syslog (LOG_ERR, "Error: failed to start collectd.");
345 break;
346 }
348 assert (0 < collectd_pid);
349 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
350 && (EINTR == errno))
351 if ((0 != loop) || (0 != restart))
352 collectd_stop ();
354 collectd_pid = 0;
356 log_status (status);
357 check_respawn ();
359 if (0 != restart) {
360 syslog (LOG_INFO, "Info: restarting collectd");
361 restart = 0;
362 }
363 else if (0 == loop)
364 syslog (LOG_WARNING, "Warning: restarting collectd");
365 }
367 syslog (LOG_INFO, "Info: shutting down collectdmon");
369 pidfile_delete ();
370 closelog ();
372 free (collectd_argv);
373 return 0;
374 } /* main */
376 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */