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