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 #if !defined(__GNUC__) || !__GNUC__
23 # define __attribute__(x) /**/
24 #endif
26 #include "config.h"
28 #include <assert.h>
30 #include <errno.h>
32 #include <fcntl.h>
34 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
39 #include <string.h>
41 #include <syslog.h>
43 #include <sys/resource.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/wait.h>
49 #include <time.h>
51 #include <unistd.h>
53 #ifndef COLLECTDMON_PIDFILE
54 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
55 #endif /* ! COLLECTDMON_PIDFILE */
57 #ifndef WCOREDUMP
58 # define WCOREDUMP(s) 0
59 #endif /* ! WCOREDUMP */
61 static int loop = 0;
62 static int restart = 0;
64 static char *pidfile = NULL;
65 static pid_t collectd_pid = 0;
67 static void exit_usage (char *name)
68 {
69 printf ("Usage: %s <options> [-- <collectd options>]\n"
71 "\nAvailable options:\n"
72 " -h Display this help and exit.\n"
73 " -c <path> Path to the collectd binary.\n"
74 " -P <file> PID-file.\n"
76 "\nFor <collectd options> see collectd.conf(5).\n"
78 "\n"PACKAGE" "VERSION", http://collectd.org/\n"
79 "by Florian octo Forster <octo@verplant.org>\n"
80 "for contributions see `AUTHORS'\n", name);
81 exit (0);
82 } /* exit_usage */
84 static int pidfile_create (void)
85 {
86 FILE *file = NULL;
88 if (NULL == pidfile)
89 pidfile = COLLECTDMON_PIDFILE;
91 if (NULL == (file = fopen (pidfile, "w"))) {
92 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
93 pidfile, strerror (errno));
94 return -1;
95 }
97 fprintf (file, "%i\n", (int)getpid ());
98 fclose (file);
99 return 0;
100 } /* pidfile_create */
102 static int pidfile_delete (void)
103 {
104 assert (NULL != pidfile);
106 if (0 != unlink (pidfile)) {
107 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
108 pidfile, strerror (errno));
109 return -1;
110 }
111 return 0;
112 } /* pidfile_remove */
114 static int daemonize (void)
115 {
116 struct rlimit rl;
118 pid_t pid = 0;
119 int i = 0;
121 if (0 != chdir ("/")) {
122 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
123 return -1;
124 }
126 if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
127 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
128 return -1;
129 }
131 if (0 > (pid = fork ())) {
132 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
133 return -1;
134 }
135 else if (pid != 0) {
136 exit (0);
137 }
139 if (0 != pidfile_create ())
140 return -1;
142 setsid ();
144 if (RLIM_INFINITY == rl.rlim_max)
145 rl.rlim_max = 1024;
147 for (i = 0; i < (int)rl.rlim_max; ++i)
148 close (i);
150 errno = 0;
151 if (open ("/dev/null", O_RDWR) != 0) {
152 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
153 strerror (errno));
154 return -1;
155 }
157 errno = 0;
158 if (dup (0) != 1) {
159 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
160 strerror (errno));
161 return -1;
162 }
164 errno = 0;
165 if (dup (0) != 2) {
166 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
167 strerror (errno));
168 return -1;
169 }
170 return 0;
171 } /* daemonize */
173 static int collectd_start (char **argv)
174 {
175 pid_t pid = 0;
177 if (0 > (pid = fork ())) {
178 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
179 return -1;
180 }
181 else if (pid != 0) {
182 collectd_pid = pid;
183 return 0;
184 }
186 execvp (argv[0], argv);
187 syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
188 argv[0], strerror (errno));
189 exit (-1);
190 } /* collectd_start */
192 static int collectd_stop (void)
193 {
194 if (0 == collectd_pid)
195 return 0;
197 if (0 != kill (collectd_pid, SIGTERM)) {
198 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
199 return -1;
200 }
201 return 0;
202 } /* collectd_stop */
204 static void sig_int_term_handler (int __attribute__((unused)) signo)
205 {
206 ++loop;
207 return;
208 } /* sig_int_term_handler */
210 static void sig_hup_handler (int __attribute__((unused)) signo)
211 {
212 ++restart;
213 return;
214 } /* sig_hup_handler */
216 static void log_status (int status)
217 {
218 if (WIFEXITED (status)) {
219 if (0 == WEXITSTATUS (status))
220 syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
221 WEXITSTATUS (status));
222 else
223 syslog (LOG_WARNING,
224 "Warning: collectd terminated with exit status %i",
225 WEXITSTATUS (status));
226 }
227 else if (WIFSIGNALED (status)) {
228 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
229 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
230 }
231 return;
232 } /* log_status */
234 static void check_respawn (void)
235 {
236 time_t t = time (NULL);
238 static time_t timestamp = 0;
239 static int counter = 0;
241 if ((t - 120) < timestamp)
242 ++counter;
243 else {
244 timestamp = t;
245 counter = 0;
246 }
248 if (10 < counter) {
249 unsigned int time_left = 300;
251 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
252 "disabled for %i seconds", time_left);
254 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
255 }
256 return;
257 } /* check_respawn */
259 int main (int argc, char **argv)
260 {
261 int collectd_argc = 0;
262 char *collectd = NULL;
263 char **collectd_argv = NULL;
265 struct sigaction sa;
267 int i = 0;
269 /* parse command line options */
270 while (42) {
271 int c = getopt (argc, argv, "hc:P:");
273 if (-1 == c)
274 break;
276 switch (c) {
277 case 'c':
278 collectd = optarg;
279 break;
280 case 'P':
281 pidfile = optarg;
282 break;
283 case 'h':
284 default:
285 exit_usage (argv[0]);
286 }
287 }
289 for (i = optind; i < argc; ++i)
290 if (0 == strcmp (argv[i], "-f"))
291 break;
293 /* i < argc => -f already present */
294 collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
295 collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
297 if (NULL == collectd_argv) {
298 fprintf (stderr, "Out of memory.");
299 return 3;
300 }
302 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
304 if (i == argc)
305 collectd_argv[collectd_argc - 1] = "-f";
307 for (i = optind; i < argc; ++i)
308 collectd_argv[i - optind + 1] = argv[i];
310 collectd_argv[collectd_argc] = NULL;
312 openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
314 if (-1 == daemonize ())
315 return 1;
317 sa.sa_handler = sig_int_term_handler;
318 sa.sa_flags = 0;
319 sigemptyset (&sa.sa_mask);
321 if (0 != sigaction (SIGINT, &sa, NULL)) {
322 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
323 return 1;
324 }
326 if (0 != sigaction (SIGTERM, &sa, NULL)) {
327 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
328 return 1;
329 }
331 sa.sa_handler = sig_hup_handler;
333 if (0 != sigaction (SIGHUP, &sa, NULL)) {
334 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
335 return 1;
336 }
338 while (0 == loop) {
339 int status = 0;
341 if (0 != collectd_start (collectd_argv)) {
342 syslog (LOG_ERR, "Error: failed to start collectd.");
343 break;
344 }
346 assert (0 < collectd_pid);
347 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
348 && (EINTR == errno))
349 if ((0 != loop) || (0 != restart))
350 collectd_stop ();
352 collectd_pid = 0;
354 log_status (status);
355 check_respawn ();
357 if (0 != restart) {
358 syslog (LOG_INFO, "Info: restarting collectd");
359 restart = 0;
360 }
361 else if (0 == loop)
362 syslog (LOG_WARNING, "Warning: restarting collectd");
363 }
365 syslog (LOG_INFO, "Info: shutting down collectdmon");
367 pidfile_delete ();
368 closelog ();
370 free (collectd_argv);
371 return 0;
372 } /* main */
374 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */