collectd: The new `FQDNLookup' option controls whether or not the FQDN should be...
[collectd.git] / src / collectd.c
1 /**
2  * collectd - src/collectd.c
3  * Copyright (C) 2005-2007  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; 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  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  *   Alvaro Barcellos <alvaro.barcellos at gmail.com>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29
30 #include "plugin.h"
31 #include "configfile.h"
32 #include "types_list.h"
33
34 /*
35  * Global variables
36  */
37 char hostname_g[DATA_MAX_NAME_LEN];
38 int  interval_g;
39 #if HAVE_LIBKSTAT
40 kstat_ctl_t *kc;
41 #endif /* HAVE_LIBKSTAT */
42
43 static int loop = 0;
44
45 static void sigIntHandler (int signal)
46 {
47         loop++;
48 }
49
50 static void sigTermHandler (int signal)
51 {
52         loop++;
53 }
54
55 static int init_hostname (void)
56 {
57         const char *str;
58
59         struct addrinfo  ai_hints;
60         struct addrinfo *ai_list;
61         struct addrinfo *ai_ptr;
62         int status;
63
64         str = global_option_get ("Hostname");
65         if (str != NULL)
66         {
67                 strncpy (hostname_g, str, sizeof (hostname_g));
68                 hostname_g[sizeof (hostname_g) - 1] = '\0';
69                 return (0);
70         }
71
72         if (gethostname (hostname_g, sizeof (hostname_g)) != 0)
73         {
74                 fprintf (stderr, "`gethostname' failed and no "
75                                 "hostname was configured.\n");
76                 return (-1);
77         }
78
79         str = global_option_get ("FQDNLookup");
80         if ((strcasecmp ("false", str) == 0)
81                         || (strcasecmp ("no", str) == 0)
82                         || (strcasecmp ("off", str) == 0))
83                 return (0);
84
85         memset (&ai_hints, '\0', sizeof (ai_hints));
86         ai_hints.ai_flags = AI_CANONNAME;
87
88         status = getaddrinfo (hostname_g, NULL, &ai_hints, &ai_list);
89         if (status != 0)
90         {
91                 ERROR ("getaddrinfo failed.");
92                 return (-1);
93         }
94
95         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
96         {
97                 if (ai_ptr->ai_canonname == NULL)
98                         continue;
99
100                 strncpy (hostname_g, ai_ptr->ai_canonname, sizeof (hostname_g));
101                 hostname_g[sizeof (hostname_g) - 1] = '\0';
102                 break;
103         }
104
105         freeaddrinfo (ai_list);
106         return (0);
107 } /* int init_hostname */
108
109 static int init_global_variables (void)
110 {
111         const char *str;
112
113         str = global_option_get ("Interval");
114         if (str == NULL)
115                 str = "10";
116         interval_g = atoi (str);
117         if (interval_g <= 0)
118         {
119                 fprintf (stderr, "Cannot set the interval to a correct value.\n"
120                                 "Please check your settings.\n");
121                 return (-1);
122         }
123         DEBUG ("interval_g = %i;", interval_g);
124
125         if (init_hostname () != 0)
126                 return (-1);
127         DEBUG ("hostname_g = %s;", hostname_g);
128
129         return (0);
130 } /* int init_global_variables */
131
132 static int change_basedir (const char *orig_dir)
133 {
134         char *dir = strdup (orig_dir);
135         int dirlen;
136         int status;
137
138         if (dir == NULL)
139         {
140                 char errbuf[1024];
141                 ERROR ("strdup failed: %s",
142                                 sstrerror (errno, errbuf, sizeof (errbuf)));
143                 return (-1);
144         }
145         
146         dirlen = strlen (dir);
147         while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
148                 dir[--dirlen] = '\0';
149
150         if (dirlen <= 0)
151                 return (-1);
152
153         status = chdir (dir);
154         free (dir);
155
156         if (status != 0)
157         {
158                 if (errno == ENOENT)
159                 {
160                         if (mkdir (orig_dir, 0755) == -1)
161                         {
162                                 char errbuf[1024];
163                                 ERROR ("change_basedir: mkdir (%s): %s", orig_dir,
164                                                 sstrerror (errno, errbuf,
165                                                         sizeof (errbuf)));
166                                 return (-1);
167                         }
168                         else if (chdir (orig_dir) == -1)
169                         {
170                                 char errbuf[1024];
171                                 ERROR ("chdir (%s): %s", orig_dir,
172                                                 sstrerror (errno, errbuf,
173                                                         sizeof (errbuf)));
174                                 return (-1);
175                         }
176                 }
177                 else
178                 {
179                         char errbuf[1024];
180                         ERROR ("chdir (%s): %s", orig_dir,
181                                         sstrerror (errno, errbuf,
182                                                 sizeof (errbuf)));
183                         return (-1);
184                 }
185         }
186
187         return (0);
188 } /* static int change_basedir (char *dir) */
189
190 #if HAVE_LIBKSTAT
191 static void update_kstat (void)
192 {
193         if (kc == NULL)
194         {
195                 if ((kc = kstat_open ()) == NULL)
196                         ERROR ("Unable to open kstat control structure");
197         }
198         else
199         {
200                 kid_t kid;
201                 kid = kstat_chain_update (kc);
202                 if (kid > 0)
203                 {
204                         INFO ("kstat chain has been updated");
205                         plugin_init_all ();
206                 }
207                 else if (kid < 0)
208                         ERROR ("kstat chain update failed");
209                 /* else: everything works as expected */
210         }
211
212         return;
213 } /* static void update_kstat (void) */
214 #endif /* HAVE_LIBKSTAT */
215
216 /* TODO
217  * Remove all settings but `-f' and `-C'
218  */
219 static void exit_usage (char *name)
220 {
221         printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
222                         
223                         "Available options:\n"
224                         "  General:\n"
225                         "    -C <file>       Configuration file.\n"
226                         "                    Default: "CONFIGFILE"\n"
227                         "    -P <file>       PID-file.\n"
228                         "                    Default: "PIDFILE"\n"
229 #if COLLECT_DAEMON
230                         "    -f              Don't fork to the background.\n"
231 #endif
232                         "\nBuiltin defaults:\n"
233                         "  Config-File       "CONFIGFILE"\n"
234                         "  PID-File          "PIDFILE"\n"
235                         "  Data-Directory    "PKGLOCALSTATEDIR"\n"
236                         "\n"PACKAGE" "VERSION", http://collectd.org/\n"
237                         "by Florian octo Forster <octo@verplant.org>\n"
238                         "for contributions see `AUTHORS'\n");
239         exit (0);
240 } /* static void exit_usage (char *name) */
241
242 static int do_init (void)
243 {
244 #if HAVE_LIBKSTAT
245         kc = NULL;
246         update_kstat ();
247 #endif
248
249 #if HAVE_LIBSTATGRAB
250         if (sg_init ())
251         {
252                 ERROR ("sg_init: %s", sg_str_error (sg_get_error ()));
253                 return (-1);
254         }
255
256         if (sg_drop_privileges ())
257         {
258                 ERROR ("sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
259                 return (-1);
260         }
261 #endif
262
263         read_types_list ();
264         plugin_init_all ();
265
266         return (0);
267 } /* int do_init () */
268
269
270 static int do_loop (void)
271 {
272         struct timeval tv_now;
273         struct timeval tv_next;
274         struct timespec ts_wait;
275
276         while (loop == 0)
277         {
278                 if (gettimeofday (&tv_next, NULL) < 0)
279                 {
280                         char errbuf[1024];
281                         ERROR ("gettimeofday failed: %s",
282                                         sstrerror (errno, errbuf,
283                                                 sizeof (errbuf)));
284                         return (-1);
285                 }
286                 tv_next.tv_sec += interval_g;
287
288 #if HAVE_LIBKSTAT
289                 update_kstat ();
290 #endif
291
292                 /* Issue all plugins */
293                 plugin_read_all (&loop);
294
295                 if (gettimeofday (&tv_now, NULL) < 0)
296                 {
297                         char errbuf[1024];
298                         ERROR ("gettimeofday failed: %s",
299                                         sstrerror (errno, errbuf,
300                                                 sizeof (errbuf)));
301                         return (-1);
302                 }
303
304                 if (timeval_sub_timespec (&tv_next, &tv_now, &ts_wait) != 0)
305                 {
306                         WARNING ("Not sleeping because "
307                                         "`timeval_sub_timespec' returned "
308                                         "non-zero!");
309                         continue;
310                 }
311
312                 while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1))
313                 {
314                         if (errno != EINTR)
315                         {
316                                 char errbuf[1024];
317                                 ERROR ("nanosleep failed: %s",
318                                                 sstrerror (errno, errbuf,
319                                                         sizeof (errbuf)));
320                                 return (-1);
321                         }
322                 }
323         } /* while (loop == 0) */
324
325         DEBUG ("return (0);");
326         return (0);
327 } /* int do_loop */
328
329 static int do_shutdown (void)
330 {
331         plugin_shutdown_all ();
332         return (0);
333 } /* int do_shutdown */
334
335 #if COLLECT_DAEMON
336 static int pidfile_create (void)
337 {
338         FILE *fh;
339         const char *file = global_option_get ("PIDFile");
340
341         if ((fh = fopen (file, "w")) == NULL)
342         {
343                 char errbuf[1024];
344                 ERROR ("fopen (%s): %s", file,
345                                 sstrerror (errno, errbuf, sizeof (errbuf)));
346                 return (1);
347         }
348
349         fprintf (fh, "%i\n", (int) getpid ());
350         fclose(fh);
351
352         return (0);
353 } /* static int pidfile_create (const char *file) */
354
355 static int pidfile_remove (void)
356 {
357         const char *file = global_option_get ("PIDFile");
358
359         DEBUG ("unlink (%s)", (file != NULL) ? file : "<null>");
360         return (unlink (file));
361 } /* static int pidfile_remove (const char *file) */
362 #endif /* COLLECT_DAEMON */
363
364 int main (int argc, char **argv)
365 {
366         struct sigaction sigIntAction;
367         struct sigaction sigTermAction;
368         char *configfile = CONFIGFILE;
369         int test_config  = 0;
370         const char *basedir;
371 #if COLLECT_DAEMON
372         struct sigaction sigChldAction;
373         pid_t pid;
374         int daemonize    = 1;
375 #endif
376
377         /* read options */
378         while (1)
379         {
380                 int c;
381
382                 c = getopt (argc, argv, "htC:"
383 #if COLLECT_DAEMON
384                                 "fP:"
385 #endif
386                 );
387
388                 if (c == -1)
389                         break;
390
391                 switch (c)
392                 {
393                         case 'C':
394                                 configfile = optarg;
395                                 break;
396                         case 't':
397                                 test_config = 1;
398                                 break;
399 #if COLLECT_DAEMON
400                         case 'P':
401                                 global_option_set ("PIDFile", optarg);
402                                 break;
403                         case 'f':
404                                 daemonize = 0;
405                                 break;
406 #endif /* COLLECT_DAEMON */
407                         case 'h':
408                         default:
409                                 exit_usage (argv[0]);
410                 } /* switch (c) */
411         } /* while (1) */
412
413         /*
414          * Read options from the config file, the environment and the command
415          * line (in that order, with later options overwriting previous ones in
416          * general).
417          * Also, this will automatically load modules.
418          */
419         if (cf_read (configfile))
420         {
421                 fprintf (stderr, "Error: Reading the config file failed!\n"
422                                 "Read the syslog for details.\n");
423                 return (1);
424         }
425
426         /*
427          * Change directory. We do this _after_ reading the config and loading
428          * modules to relative paths work as expected.
429          */
430         if ((basedir = global_option_get ("BaseDir")) == NULL)
431         {
432                 fprintf (stderr, "Don't have a basedir to use. This should not happen. Ever.");
433                 return (1);
434         }
435         else if (change_basedir (basedir))
436         {
437                 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", basedir);
438                 return (1);
439         }
440
441         /*
442          * Set global variables or, if that failes, exit. We cannot run with
443          * them being uninitialized. If nothing is configured, then defaults
444          * are being used. So this means that the user has actually done
445          * something wrong.
446          */
447         if (init_global_variables () != 0)
448                 return (1);
449
450         if (test_config)
451                 return (0);
452
453 #if COLLECT_DAEMON
454         /*
455          * fork off child
456          */
457         memset (&sigChldAction, '\0', sizeof (sigChldAction));
458         sigChldAction.sa_handler = SIG_IGN;
459         sigaction (SIGCHLD, &sigChldAction, NULL);
460
461         if (daemonize)
462         {
463                 if ((pid = fork ()) == -1)
464                 {
465                         /* error */
466                         char errbuf[1024];
467                         fprintf (stderr, "fork: %s",
468                                         sstrerror (errno, errbuf,
469                                                 sizeof (errbuf)));
470                         return (1);
471                 }
472                 else if (pid != 0)
473                 {
474                         /* parent */
475                         /* printf ("Running (PID %i)\n", pid); */
476                         return (0);
477                 }
478
479                 /* Detach from session */
480                 setsid ();
481
482                 /* Write pidfile */
483                 if (pidfile_create ())
484                         exit (2);
485
486                 /* close standard descriptors */
487                 close (2);
488                 close (1);
489                 close (0);
490
491                 if (open ("/dev/null", O_RDWR) != 0)
492                 {
493                         ERROR ("Error: Could not connect `STDIN' to `/dev/null'");
494                         return (1);
495                 }
496                 if (dup (0) != 1)
497                 {
498                         ERROR ("Error: Could not connect `STDOUT' to `/dev/null'");
499                         return (1);
500                 }
501                 if (dup (0) != 2)
502                 {
503                         ERROR ("Error: Could not connect `STDERR' to `/dev/null'");
504                         return (1);
505                 }
506         } /* if (daemonize) */
507 #endif /* COLLECT_DAEMON */
508
509         /*
510          * install signal handlers
511          */
512         memset (&sigIntAction, '\0', sizeof (sigIntAction));
513         sigIntAction.sa_handler = sigIntHandler;
514         sigaction (SIGINT, &sigIntAction, NULL);
515
516         memset (&sigTermAction, '\0', sizeof (sigTermAction));
517         sigTermAction.sa_handler = sigTermHandler;
518         sigaction (SIGTERM, &sigTermAction, NULL);
519
520         /*
521          * run the actual loops
522          */
523         do_init ();
524         do_loop ();
525
526         /* close syslog */
527         INFO ("Exiting normally");
528
529         do_shutdown ();
530
531 #if COLLECT_DAEMON
532         if (daemonize)
533                 pidfile_remove ();
534 #endif /* COLLECT_DAEMON */
535
536         return (0);
537 } /* int main */