Merge pull request #1887 from rubenk/make-xfs-optional
[collectd.git] / src / collectdmon.c
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  **/
26
27 #if !defined(__GNUC__) || !__GNUC__
28 # define __attribute__(x) /**/
29 #endif
30
31 #include "config.h"
32
33 #include <assert.h>
34
35 #include <errno.h>
36
37 #include <fcntl.h>
38
39 #include <signal.h>
40
41 #include <stdio.h>
42 #include <stdlib.h>
43
44 #include <string.h>
45
46 #include <syslog.h>
47
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>
53
54 #include <time.h>
55
56 #include <unistd.h>
57
58 #ifndef PREFIX
59 # define PREFIX "/opt/" PACKAGE_NAME
60 #endif
61
62 #ifndef LOCALSTATEDIR
63 # define LOCALSTATEDIR PREFIX "/var"
64 #endif
65
66 #ifndef COLLECTDMON_PIDFILE
67 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
68 #endif /* ! COLLECTDMON_PIDFILE */
69
70 #ifndef WCOREDUMP
71 # define WCOREDUMP(s) 0
72 #endif /* ! WCOREDUMP */
73
74 static int loop    = 0;
75 static int restart = 0;
76
77 static const char *pidfile      = NULL;
78 static pid_t       collectd_pid = 0;
79
80 __attribute__((noreturn))
81 static void exit_usage (const char *name)
82 {
83         printf ("Usage: %s <options> [-- <collectd options>]\n"
84
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"
89
90                         "\nFor <collectd options> see collectd.conf(5).\n"
91
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 */
97
98 static int pidfile_create (void)
99 {
100         FILE *file = NULL;
101
102         if (NULL == pidfile)
103                 pidfile = COLLECTDMON_PIDFILE;
104
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         }
110
111         fprintf (file, "%i\n", (int)getpid ());
112         fclose (file);
113         return 0;
114 } /* pidfile_create */
115
116 static int pidfile_delete (void)
117 {
118         assert (NULL != pidfile);
119
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 */
127
128 static int daemonize (void)
129 {
130         struct rlimit rl;
131         int dev_null;
132
133         pid_t pid = 0;
134         int   i   = 0;
135
136         if (0 != chdir ("/")) {
137                 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
138                 return -1;
139         }
140
141         if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
142                 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
143                 return -1;
144         }
145
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         }
153
154         if (0 != pidfile_create ())
155                 return -1;
156
157         setsid ();
158
159         if (RLIM_INFINITY == rl.rlim_max)
160                 rl.rlim_max = 1024;
161
162         for (i = 0; i < (int)rl.rlim_max; ++i)
163                 close (i);
164
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         }
170
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         }
176
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         }
182
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         }
188
189         if ((dev_null != STDIN_FILENO) && (dev_null != STDOUT_FILENO) && (dev_null != STDERR_FILENO))
190                 close (dev_null);
191
192         return 0;
193 } /* daemonize */
194
195 static int collectd_start (char **argv)
196 {
197         pid_t pid = 0;
198
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         }
207
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 */
213
214 static int collectd_stop (void)
215 {
216         if (0 == collectd_pid)
217                 return 0;
218
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 */
225
226 static void sig_int_term_handler (int __attribute__((unused)) signo)
227 {
228         ++loop;
229         return;
230 } /* sig_int_term_handler */
231
232 static void sig_hup_handler (int __attribute__((unused)) signo)
233 {
234         ++restart;
235         return;
236 } /* sig_hup_handler */
237
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 */
255
256 static void check_respawn (void)
257 {
258         time_t t = time (NULL);
259
260         static time_t timestamp = 0;
261         static int    counter   = 0;
262
263         if ((t - 120) < timestamp)
264                 ++counter;
265         else {
266                 timestamp = t;
267                 counter   = 0;
268         }
269
270         if (10 < counter) {
271                 unsigned int time_left = 300;
272
273                 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
274                                 "disabled for %i seconds", time_left);
275
276                 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
277         }
278         return;
279 } /* check_respawn */
280
281 int main (int argc, char **argv)
282 {
283         int    collectd_argc = 0;
284         char  *collectd      = NULL;
285         char **collectd_argv = NULL;
286
287         struct sigaction sa;
288
289         int i = 0;
290
291         /* parse command line options */
292         while (42) {
293                 int c = getopt (argc, argv, "hc:P:");
294
295                 if (-1 == c)
296                         break;
297
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         }
310
311         for (i = optind; i < argc; ++i)
312                 if (0 == strcmp (argv[i], "-f"))
313                         break;
314
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 *));
318
319         if (NULL == collectd_argv) {
320                 fprintf (stderr, "Out of memory.");
321                 return 3;
322         }
323
324         collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
325
326         if (i == argc)
327                 collectd_argv[collectd_argc - 1] = "-f";
328
329         for (i = optind; i < argc; ++i)
330                 collectd_argv[i - optind + 1] = argv[i];
331
332         collectd_argv[collectd_argc] = NULL;
333
334         openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
335
336         if (-1 == daemonize ())
337         {
338                 free (collectd_argv);
339                 return 1;
340         }
341
342         sa.sa_handler = sig_int_term_handler;
343         sa.sa_flags   = 0;
344         sigemptyset (&sa.sa_mask);
345
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         }
351
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         }
357
358         sa.sa_handler = sig_hup_handler;
359
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         }
365
366         while (0 == loop) {
367                 int status = 0;
368
369                 if (0 != collectd_start (collectd_argv)) {
370                         syslog (LOG_ERR, "Error: failed to start collectd.");
371                         break;
372                 }
373
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 ();
379
380                 collectd_pid = 0;
381
382                 log_status (status);
383                 check_respawn ();
384
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         }
392
393         syslog (LOG_INFO, "Info: shutting down collectdmon");
394
395         pidfile_delete ();
396         closelog ();
397
398         free (collectd_argv);
399         return 0;
400 } /* main */
401
402 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
403