078b2eb1d2b1422acc50370406ebf48558677a18
[collectd.git] / src / collectdmon.c
1 /**
2  * collectd - src/collectdmon.c
3  * Copyright (C) 2007  Sebastian Harl
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Sebastian Harl <sh at tokkee.org>
20  **/
21
22 #if !defined(__GNUC__) || !__GNUC__
23 # define __attribute__(x) /**/
24 #endif
25
26 #include "config.h"
27
28 #include <assert.h>
29
30 #include <errno.h>
31
32 #include <fcntl.h>
33
34 #include <signal.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38
39 #include <string.h>
40
41 #include <syslog.h>
42
43 #include <sys/resource.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/wait.h>
48
49 #include <time.h>
50
51 #include <unistd.h>
52
53 #ifndef COLLECTDMON_PIDFILE
54 # define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
55 #endif /* ! COLLECTDMON_PIDFILE */
56
57 #ifndef WCOREDUMP
58 # define WCOREDUMP(s) 0
59 #endif /* ! WCOREDUMP */
60
61 static int loop    = 0;
62 static int restart = 0;
63
64 static char  *pidfile      = NULL;
65 static pid_t  collectd_pid = 0;
66
67 static void exit_usage (char *name)
68 {
69         printf ("Usage: %s <options> [-- <collectd options>]\n"
70
71                         "\nAvailable options:\n"
72                         "  -h         Display this help and exit.\n"
73                         "  -c <path>  Path to the collectd binary.\n"
74                         "  -P <file>  PID-file.\n"
75
76                         "\nFor <collectd options> see collectd.conf(5).\n"
77
78                         "\n"PACKAGE" "VERSION", http://collectd.org/\n"
79                         "by Florian octo Forster <octo@verplant.org>\n"
80                         "for contributions see `AUTHORS'\n", name);
81         exit (0);
82 } /* exit_usage */
83
84 static int pidfile_create (void)
85 {
86         FILE *file = NULL;
87
88         if (NULL == pidfile)
89                 pidfile = COLLECTDMON_PIDFILE;
90
91         if (NULL == (file = fopen (pidfile, "w"))) {
92                 syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
93                                 pidfile, strerror (errno));
94                 return -1;
95         }
96
97         fprintf (file, "%i\n", (int)getpid ());
98         fclose (file);
99         return 0;
100 } /* pidfile_create */
101
102 static int pidfile_delete (void)
103 {
104         assert (NULL != pidfile);
105
106         if (0 != unlink (pidfile)) {
107                 syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
108                                 pidfile, strerror (errno));
109                 return -1;
110         }
111         return 0;
112 } /* pidfile_remove */
113
114 static int daemonize (void)
115 {
116         struct rlimit rl;
117
118         pid_t pid = 0;
119         int   i   = 0;
120
121         if (0 != chdir ("/")) {
122                 fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
123                 return -1;
124         }
125
126         if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
127                 fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
128                 return -1;
129         }
130
131         if (0 > (pid = fork ())) {
132                 fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
133                 return -1;
134         }
135         else if (pid != 0) {
136                 exit (0);
137         }
138
139         if (0 != pidfile_create ())
140                 return -1;
141
142         setsid ();
143
144         if (RLIM_INFINITY == rl.rlim_max)
145                 rl.rlim_max = 1024;
146
147         for (i = 0; i < (int)rl.rlim_max; ++i)
148                 close (i);
149
150         errno = 0;
151         if (open ("/dev/null", O_RDWR) != 0) {
152                 syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
153                                 strerror (errno));
154                 return -1;
155         }
156
157         errno = 0;
158         if (dup (0) != 1) {
159                 syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
160                                 strerror (errno));
161                 return -1;
162         }
163
164         errno = 0;
165         if (dup (0) != 2) {
166                 syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
167                                 strerror (errno));
168                 return -1;
169         }
170         return 0;
171 } /* daemonize */
172
173 static int collectd_start (char **argv)
174 {
175         pid_t pid = 0;
176
177         if (0 > (pid = fork ())) {
178                 syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
179                 return -1;
180         }
181         else if (pid != 0) {
182                 collectd_pid = pid;
183                 return 0;
184         }
185
186         execvp (argv[0], argv);
187         syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
188                         argv[0], strerror (errno));
189         exit (-1);
190 } /* collectd_start */
191
192 static int collectd_stop (void)
193 {
194         if (0 == collectd_pid)
195                 return 0;
196
197         if (0 != kill (collectd_pid, SIGTERM)) {
198                 syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
199                 return -1;
200         }
201         return 0;
202 } /* collectd_stop */
203
204 static void sig_int_term_handler (int __attribute__((unused)) signo)
205 {
206         ++loop;
207         return;
208 } /* sig_int_term_handler */
209
210 static void sig_hup_handler (int __attribute__((unused)) signo)
211 {
212         ++restart;
213         return;
214 } /* sig_hup_handler */
215
216 static void log_status (int status)
217 {
218         if (WIFEXITED (status)) {
219                 if (0 == WEXITSTATUS (status))
220                         syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
221                                         WEXITSTATUS (status));
222                 else
223                         syslog (LOG_WARNING,
224                                         "Warning: collectd terminated with exit status %i",
225                                         WEXITSTATUS (status));
226         }
227         else if (WIFSIGNALED (status)) {
228                 syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
229                                 WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
230         }
231         return;
232 } /* log_status */
233
234 static void check_respawn (void)
235 {
236         time_t t = time (NULL);
237
238         static time_t timestamp = 0;
239         static int    counter   = 0;
240
241         if ((t - 120) < timestamp)
242                 ++counter;
243         else {
244                 timestamp = t;
245                 counter   = 0;
246         }
247
248         if (10 < counter) {
249                 unsigned int time_left = 300;
250
251                 syslog (LOG_ERR, "Error: collectd is respawning too fast - "
252                                 "disabled for %i seconds", time_left);
253
254                 while ((0 < (time_left = sleep (time_left))) && (0 == loop));
255         }
256         return;
257 } /* check_respawn */
258
259 int main (int argc, char **argv)
260 {
261         int    collectd_argc = 0;
262         char  *collectd      = NULL;
263         char **collectd_argv = NULL;
264
265         struct sigaction sa;
266
267         int i = 0;
268
269         /* parse command line options */
270         while (42) {
271                 int c = getopt (argc, argv, "hc:P:");
272
273                 if (-1 == c)
274                         break;
275
276                 switch (c) {
277                         case 'c':
278                                 collectd = optarg;
279                                 break;
280                         case 'P':
281                                 pidfile = optarg;
282                                 break;
283                         case 'h':
284                         default:
285                                 exit_usage (argv[0]);
286                 }
287         }
288
289         for (i = optind; i < argc; ++i)
290                 if (0 == strcmp (argv[i], "-f"))
291                         break;
292
293         /* i < argc => -f already present */
294         collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
295         collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
296
297         if (NULL == collectd_argv) {
298                 fprintf (stderr, "Out of memory.");
299                 return 3;
300         }
301
302         collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
303
304         if (i == argc)
305                 collectd_argv[collectd_argc - 1] = "-f";
306
307         for (i = optind; i < argc; ++i)
308                 collectd_argv[i - optind + 1] = argv[i];
309
310         collectd_argv[collectd_argc] = NULL;
311
312         openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
313
314         if (-1 == daemonize ())
315                 return 1;
316
317         sa.sa_handler = sig_int_term_handler;
318         sa.sa_flags   = 0;
319         sigemptyset (&sa.sa_mask);
320
321         if (0 != sigaction (SIGINT, &sa, NULL)) {
322                 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
323                 return 1;
324         }
325
326         if (0 != sigaction (SIGTERM, &sa, NULL)) {
327                 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
328                 return 1;
329         }
330
331         sa.sa_handler = sig_hup_handler;
332
333         if (0 != sigaction (SIGHUP, &sa, NULL)) {
334                 syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
335                 return 1;
336         }
337
338         while (0 == loop) {
339                 int status = 0;
340
341                 if (0 != collectd_start (collectd_argv)) {
342                         syslog (LOG_ERR, "Error: failed to start collectd.");
343                         break;
344                 }
345
346                 assert (0 < collectd_pid);
347                 while ((collectd_pid != waitpid (collectd_pid, &status, 0))
348                                 && (EINTR == errno))
349                         if ((0 != loop) || (0 != restart))
350                                 collectd_stop ();
351
352                 collectd_pid = 0;
353
354                 log_status (status);
355                 check_respawn ();
356
357                 if (0 != restart) {
358                         syslog (LOG_INFO, "Info: restarting collectd");
359                         restart = 0;
360                 }
361                 else if (0 == loop)
362                         syslog (LOG_WARNING, "Warning: restarting collectd");
363         }
364
365         syslog (LOG_INFO, "Info: shutting down collectdmon");
366
367         pidfile_delete ();
368         closelog ();
369
370         free (collectd_argv);
371         return 0;
372 } /* main */
373
374 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
375