1 /**
2 * collectd - src/collectdmon.c
3 * Copyright (C) 2007 Sebastian Harl
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Sebastian Harl <sh at tokkee.org>
25 **/
27 #if !defined(__GNUC__) || !__GNUC__
28 # define __attribute__(x) /**/
29 #endif
31 #include "config.h"
33 #include <assert.h>
35 #include <errno.h>
37 #include <fcntl.h>
39 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
44 #include <string.h>
46 #include <syslog.h>
48 #include <sys/resource.h>
49 #include <sys/time.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <sys/wait.h>
54 #include <time.h>
56 #include <unistd.h>
58 #ifndef PREFIX
59 # define PREFIX "/opt/" PACKAGE_NAME
60 #endif
62 #ifndef LOCALSTATEDIR
63 # define LOCALSTATEDIR PREFIX "/var"
64 #endif
66 #ifndef COLLECTDMON_PIDFILE
67 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
68 #endif /* ! COLLECTDMON_PIDFILE */
70 #ifndef WCOREDUMP
71 # define WCOREDUMP(s) 0
72 #endif /* ! WCOREDUMP */
74 static int loop = 0;
75 static int restart = 0;
77 static const char *pidfile = NULL;
78 static pid_t collectd_pid = 0;
80 __attribute__((noreturn))
81 static void exit_usage (const char *name)
82 {
83 printf ("Usage: %s <options> [-- <collectd options>]\n"
85 "\nAvailable options:\n"
86 " -h Display this help and exit.\n"
87 " -c <path> Path to the collectd binary.\n"
88 " -P <file> PID-file.\n"
90 "\nFor <collectd options> see collectd.conf(5).\n"
92 "\n"PACKAGE_NAME" "PACKAGE_VERSION", http://collectd.org/\n"
93 "by Florian octo Forster <octo@collectd.org>\n"
94 "for contributions see `AUTHORS'\n", name);
95 exit (0);
96 } /* exit_usage */
98 static int pidfile_create (void)
99 {
100 FILE *file = NULL;
102 if (NULL == pidfile)
103 pidfile = COLLECTDMON_PIDFILE;
105 if (NULL == (file = fopen (pidfile, "w"))) {
106 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
107 pidfile, strerror (errno));
108 return -1;
109 }
111 fprintf (file, "%i\n", (int)getpid ());
112 fclose (file);
113 return 0;
114 } /* pidfile_create */
116 static int pidfile_delete (void)
117 {
118 assert (NULL != pidfile);
120 if (0 != unlink (pidfile)) {
121 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
122 pidfile, strerror (errno));
123 return -1;
124 }
125 return 0;
126 } /* pidfile_remove */
128 static int daemonize (void)
129 {
130 struct rlimit rl;
131 int dev_null;
133 pid_t pid = 0;
134 int i = 0;
136 if (0 != chdir ("/")) {
137 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
138 return -1;
139 }
141 if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
142 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
143 return -1;
144 }
146 if (0 > (pid = fork ())) {
147 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
148 return -1;
149 }
150 else if (pid != 0) {
151 exit (0);
152 }
154 if (0 != pidfile_create ())
155 return -1;
157 setsid ();
159 if (RLIM_INFINITY == rl.rlim_max)
160 rl.rlim_max = 1024;
162 for (i = 0; i < (int)rl.rlim_max; ++i)
163 close (i);
165 dev_null = open ("/dev/null", O_RDWR);
166 if (dev_null == -1) {
167 syslog (LOG_ERR, "Error: couldn't open /dev/null: %s", strerror (errno));
168 return -1;
169 }
171 if (dup2 (dev_null, STDIN_FILENO) == -1) {
172 close (dev_null);
173 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s", strerror (errno));
174 return -1;
175 }
177 if (dup2 (dev_null, STDOUT_FILENO) == -1) {
178 close (dev_null);
179 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s", strerror (errno));
180 return -1;
181 }
183 if (dup2 (dev_null, STDERR_FILENO) == -1) {
184 close (dev_null);
185 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s", strerror (errno));
186 return -1;
187 }
189 if ((dev_null != STDIN_FILENO) && (dev_null != STDOUT_FILENO) && (dev_null != STDERR_FILENO))
190 close (dev_null);
192 return 0;
193 } /* daemonize */
195 static int collectd_start (char **argv)
196 {
197 pid_t pid = 0;
199 if (0 > (pid = fork ())) {
200 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
201 return -1;
202 }
203 else if (pid != 0) {
204 collectd_pid = pid;
205 return 0;
206 }
208 execvp (argv[0], argv);
209 syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
210 argv[0], strerror (errno));
211 exit (-1);
212 } /* collectd_start */
214 static int collectd_stop (void)
215 {
216 if (0 == collectd_pid)
217 return 0;
219 if (0 != kill (collectd_pid, SIGTERM)) {
220 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
221 return -1;
222 }
223 return 0;
224 } /* collectd_stop */
226 static void sig_int_term_handler (int __attribute__((unused)) signo)
227 {
228 ++loop;
229 return;
230 } /* sig_int_term_handler */
232 static void sig_hup_handler (int __attribute__((unused)) signo)
233 {
234 ++restart;
235 return;
236 } /* sig_hup_handler */
238 static void log_status (int status)
239 {
240 if (WIFEXITED (status)) {
241 if (0 == WEXITSTATUS (status))
242 syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
243 WEXITSTATUS (status));
244 else
245 syslog (LOG_WARNING,
246 "Warning: collectd terminated with exit status %i",
247 WEXITSTATUS (status));
248 }
249 else if (WIFSIGNALED (status)) {
250 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
251 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
252 }
253 return;
254 } /* log_status */
256 static void check_respawn (void)
257 {
258 time_t t = time (NULL);
260 static time_t timestamp = 0;
261 static int counter = 0;
263 if ((t - 120) < timestamp)
264 ++counter;
265 else {
266 timestamp = t;
267 counter = 0;
268 }
270 if (10 < counter) {
271 unsigned int time_left = 300;
273 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
274 "disabled for %i seconds", time_left);
276 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
277 }
278 return;
279 } /* check_respawn */
281 int main (int argc, char **argv)
282 {
283 int collectd_argc = 0;
284 char *collectd = NULL;
285 char **collectd_argv = NULL;
287 struct sigaction sa;
289 int i = 0;
291 /* parse command line options */
292 while (42) {
293 int c = getopt (argc, argv, "hc:P:");
295 if (-1 == c)
296 break;
298 switch (c) {
299 case 'c':
300 collectd = optarg;
301 break;
302 case 'P':
303 pidfile = optarg;
304 break;
305 case 'h':
306 default:
307 exit_usage (argv[0]);
308 }
309 }
311 for (i = optind; i < argc; ++i)
312 if (0 == strcmp (argv[i], "-f"))
313 break;
315 /* i < argc => -f already present */
316 collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
317 collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
319 if (NULL == collectd_argv) {
320 fprintf (stderr, "Out of memory.");
321 return 3;
322 }
324 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
326 if (i == argc)
327 collectd_argv[collectd_argc - 1] = "-f";
329 for (i = optind; i < argc; ++i)
330 collectd_argv[i - optind + 1] = argv[i];
332 collectd_argv[collectd_argc] = NULL;
334 openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
336 if (-1 == daemonize ())
337 {
338 free (collectd_argv);
339 return 1;
340 }
342 sa.sa_handler = sig_int_term_handler;
343 sa.sa_flags = 0;
344 sigemptyset (&sa.sa_mask);
346 if (0 != sigaction (SIGINT, &sa, NULL)) {
347 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
348 free (collectd_argv);
349 return 1;
350 }
352 if (0 != sigaction (SIGTERM, &sa, NULL)) {
353 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
354 free (collectd_argv);
355 return 1;
356 }
358 sa.sa_handler = sig_hup_handler;
360 if (0 != sigaction (SIGHUP, &sa, NULL)) {
361 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
362 free (collectd_argv);
363 return 1;
364 }
366 while (0 == loop) {
367 int status = 0;
369 if (0 != collectd_start (collectd_argv)) {
370 syslog (LOG_ERR, "Error: failed to start collectd.");
371 break;
372 }
374 assert (0 < collectd_pid);
375 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
376 && (EINTR == errno))
377 if ((0 != loop) || (0 != restart))
378 collectd_stop ();
380 collectd_pid = 0;
382 log_status (status);
383 check_respawn ();
385 if (0 != restart) {
386 syslog (LOG_INFO, "Info: restarting collectd");
387 restart = 0;
388 }
389 else if (0 == loop)
390 syslog (LOG_WARNING, "Warning: restarting collectd");
391 }
393 syslog (LOG_INFO, "Info: shutting down collectdmon");
395 pidfile_delete ();
396 closelog ();
398 free (collectd_argv);
399 return 0;
400 } /* main */
402 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */