1 /**
2 * collectd - src/collectd.c
3 * Copyright (C) 2005 Florian octo Forster
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; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Florian octo Forster <octo at verplant.org>
21 * Alvaro Barcellos <alvaro.barcellos at gmail.com>
22 **/
24 #include "common.h"
25 #include "utils_debug.h"
27 #include "multicast.h"
28 #include "plugin.h"
29 #include "configfile.h"
31 #include "ping.h"
33 static int loop = 0;
35 #if HAVE_LIBKSTAT
36 kstat_ctl_t *kc;
37 #endif /* HAVE_LIBKSTAT */
39 #if COLLECT_PING
40 char *pinghosts[MAX_PINGHOSTS];
41 int num_pinghosts = 0;
42 #endif
44 /*
45 * exported variables
46 */
47 time_t curtime;
49 #if HAVE_LIBRRD
50 int operating_mode;
51 #endif
53 static void sigIntHandler (int signal)
54 {
55 loop++;
56 }
58 static int change_basedir (char *dir)
59 {
60 int dirlen = strlen (dir);
62 while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
63 dir[--dirlen] = '\0';
65 if (dirlen <= 0)
66 return (-1);
68 if (chdir (dir) == -1)
69 {
70 if (errno == ENOENT)
71 {
72 if (mkdir (dir, 0755) == -1)
73 {
74 syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
75 return (-1);
76 }
77 else if (chdir (dir) == -1)
78 {
79 syslog (LOG_ERR, "chdir (%s): %s", dir, strerror (errno));
80 return (-1);
81 }
82 }
83 else
84 {
85 syslog (LOG_ERR, "chdir: %s", strerror (errno));
86 return (-1);
87 }
88 }
90 return (0);
91 } /* static int change_basedir (char *dir) */
93 #if HAVE_LIBKSTAT
94 static void update_kstat (void)
95 {
96 if (kc == NULL)
97 {
98 if ((kc = kstat_open ()) == NULL)
99 syslog (LOG_ERR, "Unable to open kstat control structure");
100 }
101 else
102 {
103 kid_t kid;
104 kid = kstat_chain_update (kc);
105 if (kid > 0)
106 {
107 syslog (LOG_INFO, "kstat chain has been updated");
108 plugin_init_all ();
109 }
110 else if (kid < 0)
111 syslog (LOG_ERR, "kstat chain update failed");
112 /* else: everything works as expected */
113 }
115 return;
116 } /* static void update_kstat (void) */
117 #endif /* HAVE_LIBKSTAT */
119 static void exit_usage (char *name)
120 {
121 printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
123 "Available options:\n"
124 " General:\n"
125 " -C <file> Configuration file.\n"
126 " Default: "CONFIGFILE"\n"
127 #if COLLECT_DAEMON
128 " -P <file> PID file.\n"
129 " Default: "PIDFILE"\n"
130 #endif
131 " -M <dir> Module/Plugin directory.\n"
132 " Default: "PLUGINDIR"\n"
133 " -D <dir> Data storage directory.\n"
134 " Default: "PKGLOCALSTATEDIR"\n"
135 #if COLLECT_DEBUG
136 " -L <file> Log file.\n"
137 " Default: "LOGFILE"\n"
138 #endif
139 #if COLLECT_DAEMON
140 " -f Don't fork to the background.\n"
141 #endif
142 #if HAVE_LIBRRD
143 " -l Start in local mode (no network).\n"
144 " -c Start in client (sender) mode.\n"
145 " -s Start in server (listener) mode.\n"
146 #endif /* HAVE_LIBRRD */
147 #if COLLECT_PING
148 " Ping:\n"
149 " -p <host> Host to ping periodically, may be repeated to ping\n"
150 " more than one host.\n"
151 #endif /* COLLECT_PING */
152 "\n"PACKAGE" "VERSION", http://verplant.org/collectd/\n"
153 "by Florian octo Forster <octo@verplant.org>\n"
154 "for contributions see `AUTHORS'\n");
155 exit (0);
156 } /* static void exit_usage (char *name) */
158 static int start_client (void)
159 {
160 int sleepingtime;
162 #if HAVE_LIBKSTAT
163 kc = NULL;
164 update_kstat ();
165 #endif
167 #if HAVE_LIBSTATGRAB
168 if (sg_init ())
169 {
170 syslog (LOG_ERR, "sg_init: %s", sg_str_error (sg_get_error ()));
171 return (-1);
172 }
174 if (sg_drop_privileges ())
175 {
176 syslog (LOG_ERR, "sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
177 return (-1);
178 }
179 #endif
181 plugin_init_all ();
183 while (loop == 0)
184 {
185 curtime = time (NULL);
186 #if HAVE_LIBKSTAT
187 update_kstat ();
188 #endif
189 plugin_read_all ();
191 sleepingtime = 10;
192 while (sleepingtime != 0)
193 {
194 if (loop != 0)
195 break;
196 sleepingtime = sleep (sleepingtime);
197 }
198 }
200 return (0);
201 } /* static int start_client (void) */
203 #if HAVE_LIBRRD
204 static int start_server (void)
205 {
206 char *host;
207 char *type;
208 char *instance;
209 char *values;
211 while (loop == 0)
212 {
213 if (multicast_receive (&host, &type, &instance, &values) == 0)
214 plugin_write (host, type, instance, values);
216 if (host != NULL) free (host); host = NULL;
217 if (type != NULL) free (type); type = NULL;
218 if (instance != NULL) free (instance); instance = NULL;
219 if (values != NULL) free (values); values = NULL;
220 }
222 return (0);
223 } /* static int start_server (void) */
224 #endif /* HAVE_LIBRRD */
226 #if COLLECT_DAEMON
227 static int pidfile_create (const char *file)
228 {
229 FILE *fh;
231 if (file == NULL)
232 file = PIDFILE;
234 if ((fh = fopen (file, "w")) == NULL)
235 {
236 syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
237 return (1);
238 }
240 fprintf (fh, "%d\n", getpid());
241 fclose(fh);
243 return (0);
244 } /* static int pidfile_create (const char *file) */
245 #endif /* COLLECT_DAEMON */
247 #if COLLECT_DAEMON
248 static int pidfile_remove (const char *file)
249 {
250 if (file == NULL) {
251 file = PIDFILE;
252 }
253 return (unlink (file));
254 } /* static int pidfile_remove (const char *file) */
255 #endif /* COLLECT_DAEMON */
257 int main (int argc, char **argv)
258 {
259 struct sigaction sigIntAction, sigChldAction;
260 char *configfile = CONFIGFILE;
261 char *plugindir = PLUGINDIR;
262 char *datadir = PKGLOCALSTATEDIR;
263 #if COLLECT_DAEMON
264 char *pidfile = PIDFILE;
265 pid_t pid;
266 int daemonize = 1;
267 #endif
268 #if COLLECT_DEBUG
269 char *logfile = LOGFILE;
270 #endif
272 #if HAVE_LIBRRD
273 operating_mode = MODE_LOCAL;
274 #endif
276 /* open syslog */
277 openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
279 /* read options */
280 while (1)
281 {
282 int c;
284 c = getopt (argc, argv, "C:M:D:h"
285 #if COLLECT_DAEMON
286 "fP:"
287 #endif
288 #if COLLECT_DEBUG
289 "L:"
290 #endif
291 #if HAVE_LIBRRD
292 "csl"
293 #endif /* HAVE_LIBRRD */
294 #if COLLECT_PING
295 "p:"
296 #endif /* COLLECT_PING */
297 );
299 if (c == -1)
300 break;
302 switch (c)
303 {
304 #if HAVE_LIBRRD
305 case 'c':
306 operating_mode = MODE_CLIENT;
307 break;
309 case 's':
310 operating_mode = MODE_SERVER;
311 break;
313 case 'l':
314 operating_mode = MODE_LOCAL;
315 break;
316 #endif /* HAVE_LIBRRD */
317 case 'C':
318 configfile = optarg;
319 break;
320 #if COLLECT_DAEMON
321 case 'P':
322 pidfile = optarg;
323 break;
324 case 'f':
325 daemonize = 0;
326 break;
327 #endif /* COLLECT_DAEMON */
328 case 'M':
329 plugindir = optarg;
330 break;
331 case 'D':
332 datadir = optarg;
333 break;
334 #if COLLECT_DEBUG
335 case 'L':
336 logfile = optarg;
337 break;
338 #endif
339 #if COLLECT_PING
340 case 'p':
341 if (num_pinghosts < MAX_PINGHOSTS)
342 pinghosts[num_pinghosts++] = optarg;
343 else
344 fprintf (stderr, "Maximum of %i ping hosts reached.\n", MAX_PINGHOSTS);
345 break;
346 #endif /* COLLECT_PING */
347 case 'h':
348 default:
349 exit_usage (argv[0]);
350 } /* switch (c) */
351 } /* while (1) */
353 DBG_STARTFILE(logfile, "debug file opened.");
355 /*
356 * Read the config file. This will load any modules automagically.
357 */
358 plugin_set_dir (plugindir);
360 if (cf_read (configfile))
361 {
362 fprintf (stderr, "Error: Reading the config file failed!\n"
363 "Read the syslog for details.\n");
364 return (1);
365 }
367 /*
368 * Change directory. We do this _after_ reading the config and loading
369 * modules to relative paths work as expected.
370 */
371 if (change_basedir (datadir))
372 {
373 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", datadir);
374 return (1);
375 }
377 /*
378 * install signal handlers
379 */
380 sigIntAction.sa_handler = sigIntHandler;
381 sigaction (SIGINT, &sigIntAction, NULL);
383 sigChldAction.sa_handler = SIG_IGN;
384 sigaction (SIGCHLD, &sigChldAction, NULL);
386 /*
387 * fork off child
388 */
389 #if COLLECT_DAEMON
390 if (daemonize)
391 {
392 if ((pid = fork ()) == -1)
393 {
394 /* error */
395 fprintf (stderr, "fork: %s", strerror (errno));
396 return (1);
397 }
398 else if (pid != 0)
399 {
400 /* parent */
401 /* printf ("Running (PID %i)\n", pid); */
402 return (0);
403 }
405 /* Detach from session */
406 setsid ();
408 /* Write pidfile */
409 if (pidfile_create (pidfile))
410 exit (2);
412 /* close standard descriptors */
413 close (2);
414 close (1);
415 close (0);
417 if (open ("/dev/null", O_RDWR) != 0)
418 {
419 syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
420 return (1);
421 }
422 if (dup (0) != 1)
423 {
424 syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
425 return (1);
426 }
427 if (dup (0) != 2)
428 {
429 syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
430 return (1);
431 }
432 } /* if (daemonize) */
433 #endif /* COLLECT_DAEMON */
435 /*
436 * run the actual loops
437 */
438 #if HAVE_LIBRRD
439 if (operating_mode == MODE_SERVER)
440 start_server ();
441 else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL) */
442 #endif
443 start_client ();
445 DBG_STOPFILE("debug file closed.");
447 /* close syslog */
448 syslog (LOG_INFO, "Exiting normally");
449 closelog ();
451 #if COLLECT_DAEMON
452 if (daemonize)
453 pidfile_remove(pidfile);
454 #endif /* COLLECT_DAEMON */
456 return (0);
457 } /* int main (int argc, char **argv) */