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 **/
27 #if !defined(__GNUC__) || !__GNUC__
28 #define __attribute__(x) /**/
29 #endif
31 #include "config.h"
33 #include <assert.h>
35 #include <errno.h>
37 #include <fcntl.h>
39 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
44 #include <string.h>
46 #include <syslog.h>
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>
54 #include <time.h>
56 #include <unistd.h>
58 #ifndef PREFIX
59 #define PREFIX "/opt/" PACKAGE_NAME
60 #endif
62 #ifndef LOCALSTATEDIR
63 #define LOCALSTATEDIR PREFIX "/var"
64 #endif
66 #ifndef COLLECTDMON_PIDFILE
67 #define COLLECTDMON_PIDFILE LOCALSTATEDIR "/run/collectdmon.pid"
68 #endif /* ! COLLECTDMON_PIDFILE */
70 #ifndef WCOREDUMP
71 #define WCOREDUMP(s) 0
72 #endif /* ! WCOREDUMP */
74 static int loop = 0;
75 static int restart = 0;
77 static const char *pidfile = NULL;
78 static pid_t collectd_pid = 0;
80 __attribute__((noreturn)) static void exit_usage(const char *name) {
81 printf("Usage: %s <options> [-- <collectd options>]\n"
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"
88 "\nFor <collectd options> see collectd.conf(5).\n"
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 */
97 static int pidfile_create(void) {
98 FILE *file = NULL;
100 if (NULL == pidfile)
101 pidfile = COLLECTDMON_PIDFILE;
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 }
109 fprintf(file, "%i\n", (int)getpid());
110 fclose(file);
111 return 0;
112 } /* pidfile_create */
114 static int pidfile_delete(void) {
115 assert(NULL != pidfile);
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 */
125 static int daemonize(void) {
126 struct rlimit rl;
127 int dev_null;
129 pid_t pid = 0;
130 int i = 0;
132 if (0 != chdir("/")) {
133 fprintf(stderr, "Error: chdir() failed: %s\n", strerror(errno));
134 return -1;
135 }
137 if (0 != getrlimit(RLIMIT_NOFILE, &rl)) {
138 fprintf(stderr, "Error: getrlimit() failed: %s\n", strerror(errno));
139 return -1;
140 }
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 }
149 if (0 != pidfile_create())
150 return -1;
152 setsid();
154 if (RLIM_INFINITY == rl.rlim_max)
155 rl.rlim_max = 1024;
157 for (i = 0; i < (int)rl.rlim_max; ++i)
158 close(i);
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 }
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 }
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 }
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 }
187 if ((dev_null != STDIN_FILENO) && (dev_null != STDOUT_FILENO) &&
188 (dev_null != STDERR_FILENO))
189 close(dev_null);
191 return 0;
192 } /* daemonize */
194 static int collectd_start(char **argv) {
195 pid_t pid = 0;
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 }
205 execvp(argv[0], argv);
206 syslog(LOG_ERR, "Error: execvp(%s) failed: %s", argv[0], strerror(errno));
207 exit(-1);
208 } /* collectd_start */
210 static int collectd_stop(void) {
211 if (0 == collectd_pid)
212 return 0;
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 */
221 static void sig_int_term_handler(int __attribute__((unused)) signo) {
222 ++loop;
223 return;
224 } /* sig_int_term_handler */
226 static void sig_hup_handler(int __attribute__((unused)) signo) {
227 ++restart;
228 return;
229 } /* sig_hup_handler */
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 */
246 static void check_respawn(void) {
247 time_t t = time(NULL);
249 static time_t timestamp = 0;
250 static int counter = 0;
252 if ((t - 120) < timestamp)
253 ++counter;
254 else {
255 timestamp = t;
256 counter = 0;
257 }
259 if (10 < counter) {
260 unsigned int time_left = 300;
262 syslog(LOG_ERR, "Error: collectd is respawning too fast - "
263 "disabled for %i seconds",
264 time_left);
266 while ((0 < (time_left = sleep(time_left))) && (0 == loop))
267 ;
268 }
269 return;
270 } /* check_respawn */
272 int main(int argc, char **argv) {
273 int collectd_argc = 0;
274 char *collectd = NULL;
275 char **collectd_argv = NULL;
277 struct sigaction sa;
279 int i = 0;
281 /* parse command line options */
282 while (42) {
283 int c = getopt(argc, argv, "hc:P:");
285 if (-1 == c)
286 break;
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 }
301 for (i = optind; i < argc; ++i)
302 if (0 == strcmp(argv[i], "-f"))
303 break;
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 *));
309 if (NULL == collectd_argv) {
310 fprintf(stderr, "Out of memory.");
311 return 3;
312 }
314 collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
316 if (i == argc)
317 collectd_argv[collectd_argc - 1] = "-f";
319 for (i = optind; i < argc; ++i)
320 collectd_argv[i - optind + 1] = argv[i];
322 collectd_argv[collectd_argc] = NULL;
324 openlog("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
326 if (-1 == daemonize()) {
327 free(collectd_argv);
328 return 1;
329 }
331 sa.sa_handler = sig_int_term_handler;
332 sa.sa_flags = 0;
333 sigemptyset(&sa.sa_mask);
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 }
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 }
347 sa.sa_handler = sig_hup_handler;
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 }
355 while (0 == loop) {
356 int status = 0;
358 if (0 != collectd_start(collectd_argv)) {
359 syslog(LOG_ERR, "Error: failed to start collectd.");
360 break;
361 }
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();
369 collectd_pid = 0;
371 log_status(status);
372 check_respawn();
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 }
381 syslog(LOG_INFO, "Info: shutting down collectdmon");
383 pidfile_delete();
384 closelog();
386 free(collectd_argv);
387 return 0;
388 } /* main */
390 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */