Merge branch 'collectd-5.5'
[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 COLLECTDMON_PIDFILE
59 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
60 #endif /* ! COLLECTDMON_PIDFILE */
61
62 #ifndef WCOREDUMP
63 # define WCOREDUMP(s) 0
64 #endif /* ! WCOREDUMP */
65
66 static int loop    = 0;
67 static int restart = 0;
68
69 static char  *pidfile      = NULL;
70 static pid_t  collectd_pid = 0;
71
72 static void exit_usage (char *name)
73 {
74         printf ("Usage: %s <options> [-- <collectd options>]\n"
75
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"
80
81                         "\nFor <collectd options> see collectd.conf(5).\n"
82
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 */
88
89 static int pidfile_create (void)
90 {
91         FILE *file = NULL;
92
93         if (NULL == pidfile)
94                 pidfile = COLLECTDMON_PIDFILE;
95
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         }
101
102         fprintf (file, "%i\n", (int)getpid ());
103         fclose (file);
104         return 0;
105 } /* pidfile_create */
106
107 static int pidfile_delete (void)
108 {
109         assert (NULL != pidfile);
110
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 */
118
119 static int daemonize (void)
120 {
121         struct rlimit rl;
122         int status;
123
124         pid_t pid = 0;
125         int   i   = 0;
126
127         if (0 != chdir ("/")) {
128                 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
129                 return -1;
130         }
131
132         if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
133                 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
134                 return -1;
135         }
136
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         }
144
145         if (0 != pidfile_create ())
146                 return -1;
147
148         setsid ();
149
150         if (RLIM_INFINITY == rl.rlim_max)
151                 rl.rlim_max = 1024;
152
153         for (i = 0; i < (int)rl.rlim_max; ++i)
154                 close (i);
155
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         }
163
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         }
171
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 */
181
182 static int collectd_start (char **argv)
183 {
184         pid_t pid = 0;
185
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         }
194
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 */
200
201 static int collectd_stop (void)
202 {
203         if (0 == collectd_pid)
204                 return 0;
205
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 */
212
213 static void sig_int_term_handler (int __attribute__((unused)) signo)
214 {
215         ++loop;
216         return;
217 } /* sig_int_term_handler */
218
219 static void sig_hup_handler (int __attribute__((unused)) signo)
220 {
221         ++restart;
222         return;
223 } /* sig_hup_handler */
224
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 */
242
243 static void check_respawn (void)
244 {
245         time_t t = time (NULL);
246
247         static time_t timestamp = 0;
248         static int    counter   = 0;
249
250         if ((t - 120) < timestamp)
251                 ++counter;
252         else {
253                 timestamp = t;
254                 counter   = 0;
255         }
256
257         if (10 < counter) {
258                 unsigned int time_left = 300;
259
260                 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
261                                 "disabled for %i seconds", time_left);
262
263                 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
264         }
265         return;
266 } /* check_respawn */
267
268 int main (int argc, char **argv)
269 {
270         int    collectd_argc = 0;
271         char  *collectd      = NULL;
272         char **collectd_argv = NULL;
273
274         struct sigaction sa;
275
276         int i = 0;
277
278         /* parse command line options */
279         while (42) {
280                 int c = getopt (argc, argv, "hc:P:");
281
282                 if (-1 == c)
283                         break;
284
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         }
297
298         for (i = optind; i < argc; ++i)
299                 if (0 == strcmp (argv[i], "-f"))
300                         break;
301
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 *));
305
306         if (NULL == collectd_argv) {
307                 fprintf (stderr, "Out of memory.");
308                 return 3;
309         }
310
311         collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
312
313         if (i == argc)
314                 collectd_argv[collectd_argc - 1] = "-f";
315
316         for (i = optind; i < argc; ++i)
317                 collectd_argv[i - optind + 1] = argv[i];
318
319         collectd_argv[collectd_argc] = NULL;
320
321         openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
322
323         if (-1 == daemonize ())
324         {
325                 free (collectd_argv);
326                 return 1;
327         }
328
329         sa.sa_handler = sig_int_term_handler;
330         sa.sa_flags   = 0;
331         sigemptyset (&sa.sa_mask);
332
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         }
338
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         }
344
345         sa.sa_handler = sig_hup_handler;
346
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         }
352
353         while (0 == loop) {
354                 int status = 0;
355
356                 if (0 != collectd_start (collectd_argv)) {
357                         syslog (LOG_ERR, "Error: failed to start collectd.");
358                         break;
359                 }
360
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 ();
366
367                 collectd_pid = 0;
368
369                 log_status (status);
370                 check_respawn ();
371
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         }
379
380         syslog (LOG_INFO, "Info: shutting down collectdmon");
381
382         pidfile_delete ();
383         closelog ();
384
385         free (collectd_argv);
386         return 0;
387 } /* main */
388
389 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
390