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 COLLECTDMON_PIDFILE
59 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
60 #endif /* ! COLLECTDMON_PIDFILE */
62 #ifndef WCOREDUMP
63 # define WCOREDUMP(s) 0
64 #endif /* ! WCOREDUMP */
66 static int loop = 0;
67 static int restart = 0;
69 static const char *pidfile = NULL;
70 static pid_t collectd_pid = 0;
72 __attribute__((noreturn))
73 static void exit_usage (const char *name)
74 {
75 printf ("Usage: %s <options> [-- <collectd options>]\n"
77 "\nAvailable options:\n"
78 " -h Display this help and exit.\n"
79 " -c <path> Path to the collectd binary.\n"
80 " -P <file> PID-file.\n"
82 "\nFor <collectd options> see collectd.conf(5).\n"
84 "\n"PACKAGE_NAME" "PACKAGE_VERSION", http://collectd.org/\n"
85 "by Florian octo Forster <octo@collectd.org>\n"
86 "for contributions see `AUTHORS'\n", name);
87 exit (0);
88 } /* exit_usage */
90 static int pidfile_create (void)
91 {
92 FILE *file = NULL;
94 if (NULL == pidfile)
95 pidfile = COLLECTDMON_PIDFILE;
97 if (NULL == (file = fopen (pidfile, "w"))) {
98 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
99 pidfile, strerror (errno));
100 return -1;
101 }
103 fprintf (file, "%i\n", (int)getpid ());
104 fclose (file);
105 return 0;
106 } /* pidfile_create */
108 static int pidfile_delete (void)
109 {
110 assert (NULL != pidfile);
112 if (0 != unlink (pidfile)) {
113 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
114 pidfile, strerror (errno));
115 return -1;
116 }
117 return 0;
118 } /* pidfile_remove */
120 static int daemonize (void)
121 {
122 struct rlimit rl;
123 int dev_null;
125 pid_t pid = 0;
126 int i = 0;
128 if (0 != chdir ("/")) {
129 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
130 return -1;
131 }
133 if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
134 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
135 return -1;
136 }
138 if (0 > (pid = fork ())) {
139 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
140 return -1;
141 }
142 else if (pid != 0) {
143 exit (0);
144 }
146 if (0 != pidfile_create ())
147 return -1;
149 setsid ();
151 if (RLIM_INFINITY == rl.rlim_max)
152 rl.rlim_max = 1024;
154 for (i = 0; i < (int)rl.rlim_max; ++i)
155 close (i);
157 dev_null = open ("/dev/null", O_RDWR);
158 if (dev_null == -1) {
159 syslog (LOG_ERR, "Error: couldn't failed to open /dev/null: %s", strerror (errno));
160 return -1;
161 }
163 if (dup2 (dev_null, STDIN_FILENO) == -1) {
164 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s", strerror (errno));
165 return -1;
166 }
168 if (dup2 (dev_null, STDOUT_FILENO) == -1) {
169 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s", strerror (errno));
170 return -1;
171 }
173 if (dup2 (dev_null, STDERR_FILENO) == -1) {
174 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s", strerror (errno));
175 return -1;
176 }
178 if ((dev_null != STDIN_FILENO) && (dev_null != STDOUT_FILENO) && (dev_null != STDERR_FILENO))
179 close (dev_null);
181 return 0;
182 } /* daemonize */
184 static int collectd_start (char **argv)
185 {
186 pid_t pid = 0;
188 if (0 > (pid = fork ())) {
189 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
190 return -1;
191 }
192 else if (pid != 0) {
193 collectd_pid = pid;
194 return 0;
195 }
197 execvp (argv[0], argv);
198 syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
199 argv[0], strerror (errno));
200 exit (-1);
201 } /* collectd_start */
203 static int collectd_stop (void)
204 {
205 if (0 == collectd_pid)
206 return 0;
208 if (0 != kill (collectd_pid, SIGTERM)) {
209 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
210 return -1;
211 }
212 return 0;
213 } /* collectd_stop */
215 static void sig_int_term_handler (int __attribute__((unused)) signo)
216 {
217 ++loop;
218 return;
219 } /* sig_int_term_handler */
221 static void sig_hup_handler (int __attribute__((unused)) signo)
222 {
223 ++restart;
224 return;
225 } /* sig_hup_handler */
227 static void log_status (int status)
228 {
229 if (WIFEXITED (status)) {
230 if (0 == WEXITSTATUS (status))
231 syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
232 WEXITSTATUS (status));
233 else
234 syslog (LOG_WARNING,
235 "Warning: collectd terminated with exit status %i",
236 WEXITSTATUS (status));
237 }
238 else if (WIFSIGNALED (status)) {
239 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
240 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
241 }
242 return;
243 } /* log_status */
245 static void check_respawn (void)
246 {
247 time_t t = time (NULL);
249 static time_t timestamp = 0;
250 static int counter = 0;
252 if ((t - 120) < timestamp)
253 ++counter;
254 else {
255 timestamp = t;
256 counter = 0;
257 }
259 if (10 < counter) {
260 unsigned int time_left = 300;
262 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
263 "disabled for %i seconds", time_left);
265 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
266 }
267 return;
268 } /* check_respawn */
270 int main (int argc, char **argv)
271 {
272 int collectd_argc = 0;
273 char *collectd = NULL;
274 char **collectd_argv = NULL;
276 struct sigaction sa;
278 int i = 0;
280 /* parse command line options */
281 while (42) {
282 int c = getopt (argc, argv, "hc:P:");
284 if (-1 == c)
285 break;
287 switch (c) {
288 case 'c':
289 collectd = optarg;
290 break;
291 case 'P':
292 pidfile = optarg;
293 break;
294 case 'h':
295 default:
296 exit_usage (argv[0]);
297 }
298 }
300 for (i = optind; i < argc; ++i)
301 if (0 == strcmp (argv[i], "-f"))
302 break;
304 /* i < argc => -f already present */
305 collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
306 collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
308 if (NULL == collectd_argv) {
309 fprintf (stderr, "Out of memory.");
310 return 3;
311 }
313 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
315 if (i == argc)
316 collectd_argv[collectd_argc - 1] = "-f";
318 for (i = optind; i < argc; ++i)
319 collectd_argv[i - optind + 1] = argv[i];
321 collectd_argv[collectd_argc] = NULL;
323 openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
325 if (-1 == daemonize ())
326 {
327 free (collectd_argv);
328 return 1;
329 }
331 sa.sa_handler = sig_int_term_handler;
332 sa.sa_flags = 0;
333 sigemptyset (&sa.sa_mask);
335 if (0 != sigaction (SIGINT, &sa, NULL)) {
336 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
337 free (collectd_argv);
338 return 1;
339 }
341 if (0 != sigaction (SIGTERM, &sa, NULL)) {
342 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
343 free (collectd_argv);
344 return 1;
345 }
347 sa.sa_handler = sig_hup_handler;
349 if (0 != sigaction (SIGHUP, &sa, NULL)) {
350 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
351 free (collectd_argv);
352 return 1;
353 }
355 while (0 == loop) {
356 int status = 0;
358 if (0 != collectd_start (collectd_argv)) {
359 syslog (LOG_ERR, "Error: failed to start collectd.");
360 break;
361 }
363 assert (0 < collectd_pid);
364 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
365 && (EINTR == errno))
366 if ((0 != loop) || (0 != restart))
367 collectd_stop ();
369 collectd_pid = 0;
371 log_status (status);
372 check_respawn ();
374 if (0 != restart) {
375 syslog (LOG_INFO, "Info: restarting collectd");
376 restart = 0;
377 }
378 else if (0 == loop)
379 syslog (LOG_WARNING, "Warning: restarting collectd");
380 }
382 syslog (LOG_INFO, "Info: shutting down collectdmon");
384 pidfile_delete ();
385 closelog ();
387 free (collectd_argv);
388 return 0;
389 } /* main */
391 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */