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 char *pidfile = NULL;
70 static pid_t collectd_pid = 0;
72 static void exit_usage (char *name)
73 {
74 printf ("Usage: %s <options> [-- <collectd options>]\n"
76 "\nAvailable options:\n"
77 " -h Display this help and exit.\n"
78 " -c <path> Path to the collectd binary.\n"
79 " -P <file> PID-file.\n"
81 "\nFor <collectd options> see collectd.conf(5).\n"
83 "\n"PACKAGE_NAME" "PACKAGE_VERSION", http://collectd.org/\n"
84 "by Florian octo Forster <octo@collectd.org>\n"
85 "for contributions see `AUTHORS'\n", name);
86 exit (0);
87 } /* exit_usage */
89 static int pidfile_create (void)
90 {
91 FILE *file = NULL;
93 if (NULL == pidfile)
94 pidfile = COLLECTDMON_PIDFILE;
96 if (NULL == (file = fopen (pidfile, "w"))) {
97 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
98 pidfile, strerror (errno));
99 return -1;
100 }
102 fprintf (file, "%i\n", (int)getpid ());
103 fclose (file);
104 return 0;
105 } /* pidfile_create */
107 static int pidfile_delete (void)
108 {
109 assert (NULL != pidfile);
111 if (0 != unlink (pidfile)) {
112 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
113 pidfile, strerror (errno));
114 return -1;
115 }
116 return 0;
117 } /* pidfile_remove */
119 static int daemonize (void)
120 {
121 struct rlimit rl;
122 int status;
124 pid_t pid = 0;
125 int i = 0;
127 if (0 != chdir ("/")) {
128 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
129 return -1;
130 }
132 if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
133 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
134 return -1;
135 }
137 if (0 > (pid = fork ())) {
138 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
139 return -1;
140 }
141 else if (pid != 0) {
142 exit (0);
143 }
145 if (0 != pidfile_create ())
146 return -1;
148 setsid ();
150 if (RLIM_INFINITY == rl.rlim_max)
151 rl.rlim_max = 1024;
153 for (i = 0; i < (int)rl.rlim_max; ++i)
154 close (i);
156 errno = 0;
157 status = open ("/dev/null", O_RDWR);
158 if (status != 0) {
159 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
160 strerror (errno));
161 return -1;
162 }
164 errno = 0;
165 status = dup (0);
166 if (status != 1) {
167 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
168 strerror (errno));
169 return -1;
170 }
172 errno = 0;
173 status = dup (0);
174 if (status != 2) {
175 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
176 strerror (errno));
177 return -1;
178 }
179 return 0;
180 } /* daemonize */
182 static int collectd_start (char **argv)
183 {
184 pid_t pid = 0;
186 if (0 > (pid = fork ())) {
187 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
188 return -1;
189 }
190 else if (pid != 0) {
191 collectd_pid = pid;
192 return 0;
193 }
195 execvp (argv[0], argv);
196 syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
197 argv[0], strerror (errno));
198 exit (-1);
199 } /* collectd_start */
201 static int collectd_stop (void)
202 {
203 if (0 == collectd_pid)
204 return 0;
206 if (0 != kill (collectd_pid, SIGTERM)) {
207 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
208 return -1;
209 }
210 return 0;
211 } /* collectd_stop */
213 static void sig_int_term_handler (int __attribute__((unused)) signo)
214 {
215 ++loop;
216 return;
217 } /* sig_int_term_handler */
219 static void sig_hup_handler (int __attribute__((unused)) signo)
220 {
221 ++restart;
222 return;
223 } /* sig_hup_handler */
225 static void log_status (int status)
226 {
227 if (WIFEXITED (status)) {
228 if (0 == WEXITSTATUS (status))
229 syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
230 WEXITSTATUS (status));
231 else
232 syslog (LOG_WARNING,
233 "Warning: collectd terminated with exit status %i",
234 WEXITSTATUS (status));
235 }
236 else if (WIFSIGNALED (status)) {
237 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
238 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
239 }
240 return;
241 } /* log_status */
243 static void check_respawn (void)
244 {
245 time_t t = time (NULL);
247 static time_t timestamp = 0;
248 static int counter = 0;
250 if ((t - 120) < timestamp)
251 ++counter;
252 else {
253 timestamp = t;
254 counter = 0;
255 }
257 if (10 < counter) {
258 unsigned int time_left = 300;
260 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
261 "disabled for %i seconds", time_left);
263 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
264 }
265 return;
266 } /* check_respawn */
268 int main (int argc, char **argv)
269 {
270 int collectd_argc = 0;
271 char *collectd = NULL;
272 char **collectd_argv = NULL;
274 struct sigaction sa;
276 int i = 0;
278 /* parse command line options */
279 while (42) {
280 int c = getopt (argc, argv, "hc:P:");
282 if (-1 == c)
283 break;
285 switch (c) {
286 case 'c':
287 collectd = optarg;
288 break;
289 case 'P':
290 pidfile = optarg;
291 break;
292 case 'h':
293 default:
294 exit_usage (argv[0]);
295 }
296 }
298 for (i = optind; i < argc; ++i)
299 if (0 == strcmp (argv[i], "-f"))
300 break;
302 /* i < argc => -f already present */
303 collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
304 collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
306 if (NULL == collectd_argv) {
307 fprintf (stderr, "Out of memory.");
308 return 3;
309 }
311 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
313 if (i == argc)
314 collectd_argv[collectd_argc - 1] = "-f";
316 for (i = optind; i < argc; ++i)
317 collectd_argv[i - optind + 1] = argv[i];
319 collectd_argv[collectd_argc] = NULL;
321 openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
323 if (-1 == daemonize ())
324 {
325 free (collectd_argv);
326 return 1;
327 }
329 sa.sa_handler = sig_int_term_handler;
330 sa.sa_flags = 0;
331 sigemptyset (&sa.sa_mask);
333 if (0 != sigaction (SIGINT, &sa, NULL)) {
334 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
335 free (collectd_argv);
336 return 1;
337 }
339 if (0 != sigaction (SIGTERM, &sa, NULL)) {
340 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
341 free (collectd_argv);
342 return 1;
343 }
345 sa.sa_handler = sig_hup_handler;
347 if (0 != sigaction (SIGHUP, &sa, NULL)) {
348 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
349 free (collectd_argv);
350 return 1;
351 }
353 while (0 == loop) {
354 int status = 0;
356 if (0 != collectd_start (collectd_argv)) {
357 syslog (LOG_ERR, "Error: failed to start collectd.");
358 break;
359 }
361 assert (0 < collectd_pid);
362 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
363 && (EINTR == errno))
364 if ((0 != loop) || (0 != restart))
365 collectd_stop ();
367 collectd_pid = 0;
369 log_status (status);
370 check_respawn ();
372 if (0 != restart) {
373 syslog (LOG_INFO, "Info: restarting collectd");
374 restart = 0;
375 }
376 else if (0 == loop)
377 syslog (LOG_WARNING, "Warning: restarting collectd");
378 }
380 syslog (LOG_INFO, "Info: shutting down collectdmon");
382 pidfile_delete ();
383 closelog ();
385 free (collectd_argv);
386 return 0;
387 } /* main */
389 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */