Merge branch 'collectd-5.7' into collectd-5.8
[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/stat.h>
50 #include <sys/time.h>
51 #include <sys/types.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)) static void exit_usage(const char *name) {
81   printf("Usage: %s <options> [-- <collectd options>]\n"
82
83          "\nAvailable options:\n"
84          "  -h         Display this help and exit.\n"
85          "  -c <path>  Path to the collectd binary.\n"
86          "  -P <file>  PID-file.\n"
87
88          "\nFor <collectd options> see collectd.conf(5).\n"
89
90          "\n" PACKAGE_NAME " " PACKAGE_VERSION ", http://collectd.org/\n"
91          "by Florian octo Forster <octo@collectd.org>\n"
92          "for contributions see `AUTHORS'\n",
93          name);
94   exit(0);
95 } /* exit_usage */
96
97 static int pidfile_create(void) {
98   FILE *file = NULL;
99
100   if (NULL == pidfile)
101     pidfile = COLLECTDMON_PIDFILE;
102
103   if (NULL == (file = fopen(pidfile, "w"))) {
104     syslog(LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
105            pidfile, strerror(errno));
106     return -1;
107   }
108
109   fprintf(file, "%i\n", (int)getpid());
110   fclose(file);
111   return 0;
112 } /* pidfile_create */
113
114 static int pidfile_delete(void) {
115   assert(NULL != pidfile);
116
117   if (0 != unlink(pidfile)) {
118     syslog(LOG_ERR, "Error: couldn't delete PID-file (%s): %s", pidfile,
119            strerror(errno));
120     return -1;
121   }
122   return 0;
123 } /* pidfile_remove */
124
125 static int daemonize(void) {
126   struct rlimit rl;
127   int dev_null;
128
129   pid_t pid = 0;
130   int i = 0;
131
132   if (0 != chdir("/")) {
133     fprintf(stderr, "Error: chdir() failed: %s\n", strerror(errno));
134     return -1;
135   }
136
137   if (0 != getrlimit(RLIMIT_NOFILE, &rl)) {
138     fprintf(stderr, "Error: getrlimit() failed: %s\n", strerror(errno));
139     return -1;
140   }
141
142   if (0 > (pid = fork())) {
143     fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
144     return -1;
145   } else if (pid != 0) {
146     exit(0);
147   }
148
149   if (0 != pidfile_create())
150     return -1;
151
152   setsid();
153
154   if (RLIM_INFINITY == rl.rlim_max)
155     rl.rlim_max = 1024;
156
157   for (i = 0; i < (int)rl.rlim_max; ++i)
158     close(i);
159
160   dev_null = open("/dev/null", O_RDWR);
161   if (dev_null == -1) {
162     syslog(LOG_ERR, "Error: couldn't open /dev/null: %s", strerror(errno));
163     return -1;
164   }
165
166   if (dup2(dev_null, STDIN_FILENO) == -1) {
167     close(dev_null);
168     syslog(LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
169            strerror(errno));
170     return -1;
171   }
172
173   if (dup2(dev_null, STDOUT_FILENO) == -1) {
174     close(dev_null);
175     syslog(LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
176            strerror(errno));
177     return -1;
178   }
179
180   if (dup2(dev_null, STDERR_FILENO) == -1) {
181     close(dev_null);
182     syslog(LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
183            strerror(errno));
184     return -1;
185   }
186
187   if ((dev_null != STDIN_FILENO) && (dev_null != STDOUT_FILENO) &&
188       (dev_null != STDERR_FILENO))
189     close(dev_null);
190
191   return 0;
192 } /* daemonize */
193
194 static int collectd_start(char **argv) {
195   pid_t pid = 0;
196
197   if (0 > (pid = fork())) {
198     syslog(LOG_ERR, "Error: fork() failed: %s", strerror(errno));
199     return -1;
200   } else if (pid != 0) {
201     collectd_pid = pid;
202     return 0;
203   }
204
205   execvp(argv[0], argv);
206   syslog(LOG_ERR, "Error: execvp(%s) failed: %s", argv[0], strerror(errno));
207   exit(-1);
208 } /* collectd_start */
209
210 static int collectd_stop(void) {
211   if (0 == collectd_pid)
212     return 0;
213
214   if (0 != kill(collectd_pid, SIGTERM)) {
215     syslog(LOG_ERR, "Error: kill() failed: %s", strerror(errno));
216     return -1;
217   }
218   return 0;
219 } /* collectd_stop */
220
221 static void sig_int_term_handler(int __attribute__((unused)) signo) {
222   ++loop;
223   return;
224 } /* sig_int_term_handler */
225
226 static void sig_hup_handler(int __attribute__((unused)) signo) {
227   ++restart;
228   return;
229 } /* sig_hup_handler */
230
231 static void log_status(int status) {
232   if (WIFEXITED(status)) {
233     if (0 == WEXITSTATUS(status))
234       syslog(LOG_INFO, "Info: collectd terminated with exit status %i",
235              WEXITSTATUS(status));
236     else
237       syslog(LOG_WARNING, "Warning: collectd terminated with exit status %i",
238              WEXITSTATUS(status));
239   } else if (WIFSIGNALED(status)) {
240     syslog(LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
241            WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : "");
242   }
243   return;
244 } /* log_status */
245
246 static void check_respawn(void) {
247   time_t t = time(NULL);
248
249   static time_t timestamp = 0;
250   static int counter = 0;
251
252   if ((t - 120) < timestamp)
253     ++counter;
254   else {
255     timestamp = t;
256     counter = 0;
257   }
258
259   if (10 < counter) {
260     unsigned int time_left = 300;
261
262     syslog(LOG_ERR, "Error: collectd is respawning too fast - "
263                     "disabled for %i seconds",
264            time_left);
265
266     while ((0 < (time_left = sleep(time_left))) && (0 == loop))
267       ;
268   }
269   return;
270 } /* check_respawn */
271
272 int main(int argc, char **argv) {
273   int collectd_argc = 0;
274   char *collectd = NULL;
275   char **collectd_argv = NULL;
276
277   struct sigaction sa;
278
279   int i = 0;
280
281   /* parse command line options */
282   while (42) {
283     int c = getopt(argc, argv, "hc:P:");
284
285     if (-1 == c)
286       break;
287
288     switch (c) {
289     case 'c':
290       collectd = optarg;
291       break;
292     case 'P':
293       pidfile = optarg;
294       break;
295     case 'h':
296     default:
297       exit_usage(argv[0]);
298     }
299   }
300
301   for (i = optind; i < argc; ++i)
302     if (0 == strcmp(argv[i], "-f"))
303       break;
304
305   /* i < argc => -f already present */
306   collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
307   collectd_argv = (char **)calloc(collectd_argc + 1, sizeof(char *));
308
309   if (NULL == collectd_argv) {
310     fprintf(stderr, "Out of memory.");
311     return 3;
312   }
313
314   collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
315
316   if (i == argc)
317     collectd_argv[collectd_argc - 1] = "-f";
318
319   for (i = optind; i < argc; ++i)
320     collectd_argv[i - optind + 1] = argv[i];
321
322   collectd_argv[collectd_argc] = NULL;
323
324   openlog("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
325
326   if (-1 == daemonize()) {
327     free(collectd_argv);
328     return 1;
329   }
330
331   sa.sa_handler = sig_int_term_handler;
332   sa.sa_flags = 0;
333   sigemptyset(&sa.sa_mask);
334
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   }
340
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   }
346
347   sa.sa_handler = sig_hup_handler;
348
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   }
354
355   while (0 == loop) {
356     int status = 0;
357
358     if (0 != collectd_start(collectd_argv)) {
359       syslog(LOG_ERR, "Error: failed to start collectd.");
360       break;
361     }
362
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();
368
369     collectd_pid = 0;
370
371     log_status(status);
372     check_respawn();
373
374     if (0 != restart) {
375       syslog(LOG_INFO, "Info: restarting collectd");
376       restart = 0;
377     } else if (0 == loop)
378       syslog(LOG_WARNING, "Warning: restarting collectd");
379   }
380
381   syslog(LOG_INFO, "Info: shutting down collectdmon");
382
383   pidfile_delete();
384   closelog();
385
386   free(collectd_argv);
387   return 0;
388 } /* main */