Code

ab5564ead694502e0dd510804a406a1bf4e7376c
[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  **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "utils_debug.h"
27 #include "network.h"
28 #include "plugin.h"
29 #include "configfile.h"
31 static int loop = 0;
33 #if HAVE_LIBKSTAT
34 kstat_ctl_t *kc;
35 #endif /* HAVE_LIBKSTAT */
37 /*
38  * exported variables
39  */
40 time_t curtime;
42 static void sigIntHandler (int signal)
43 {
44         loop++;
45 }
47 static void sigTermHandler (int signal)
48 {
49         loop++;
50 }
52 static int change_basedir (const char *orig_dir)
53 {
54         char *dir = strdup (orig_dir);
55         int dirlen;
56         int status;
58         if (dir == NULL)
59         {
60                 syslog (LOG_ERR, "strdup failed: %s", strerror (errno));
61                 return (-1);
62         }
63         
64         dirlen = strlen (dir);
65         while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
66                 dir[--dirlen] = '\0';
68         if (dirlen <= 0)
69                 return (-1);
71         status = chdir (dir);
72         free (dir);
74         if (status != 0)
75         {
76                 if (errno == ENOENT)
77                 {
78                         if (mkdir (orig_dir, 0755) == -1)
79                         {
80                                 syslog (LOG_ERR, "mkdir (%s): %s", orig_dir,
81                                                 strerror (errno));
82                                 return (-1);
83                         }
84                         else if (chdir (orig_dir) == -1)
85                         {
86                                 syslog (LOG_ERR, "chdir (%s): %s", orig_dir,
87                                                 strerror (errno));
88                                 return (-1);
89                         }
90                 }
91                 else
92                 {
93                         syslog (LOG_ERR, "chdir (%s): %s", orig_dir,
94                                         strerror (errno));
95                         return (-1);
96                 }
97         }
99         return (0);
100 } /* static int change_basedir (char *dir) */
102 #if HAVE_LIBKSTAT
103 static void update_kstat (void)
105         if (kc == NULL)
106         {
107                 if ((kc = kstat_open ()) == NULL)
108                         syslog (LOG_ERR, "Unable to open kstat control structure");
109         }
110         else
111         {
112                 kid_t kid;
113                 kid = kstat_chain_update (kc);
114                 if (kid > 0)
115                 {
116                         syslog (LOG_INFO, "kstat chain has been updated");
117                         plugin_init_all ();
118                 }
119                 else if (kid < 0)
120                         syslog (LOG_ERR, "kstat chain update failed");
121                 /* else: everything works as expected */
122         }
124         return;
125 } /* static void update_kstat (void) */
126 #endif /* HAVE_LIBKSTAT */
128 /* TODO
129  * Remove all settings but `-f' and `-C'
130  */
131 static void exit_usage (char *name)
133         printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
134                         
135                         "Available options:\n"
136                         "  General:\n"
137                         "    -C <file>       Configuration file.\n"
138                         "                    Default: "CONFIGFILE"\n"
139                         "    -P <file>       PID-file.\n"
140                         "                    Default: "PIDFILE"\n"
141 #if COLLECT_DAEMON
142                         "    -f              Don't fork to the background.\n"
143 #endif
144                         "\nBuiltin defaults:\n"
145                         "  Config-File       "CONFIGFILE"\n"
146                         "  PID-File          "PIDFILE"\n"
147                         "  Data-Directory    "PKGLOCALSTATEDIR"\n"
148 #if COLLECT_DEBUG
149                         "  Log-File          "LOGFILE"\n"
150 #endif
151                         "  Step              "COLLECTD_STEP" seconds\n"
152                         "  Heartbeat         "COLLECTD_HEARTBEAT" seconds\n"
153                         "\n"PACKAGE" "VERSION", http://collectd.org/\n"
154                         "by Florian octo Forster <octo@verplant.org>\n"
155                         "for contributions see `AUTHORS'\n");
156         exit (0);
157 } /* static void exit_usage (char *name) */
159 static int do_init (void)
161 #if HAVE_LIBKSTAT
162         kc = NULL;
163         update_kstat ();
164 #endif
166 #if HAVE_LIBSTATGRAB
167         if (sg_init ())
168         {
169                 syslog (LOG_ERR, "sg_init: %s", sg_str_error (sg_get_error ()));
170                 return (-1);
171         }
173         if (sg_drop_privileges ())
174         {
175                 syslog (LOG_ERR, "sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
176                 return (-1);
177         }
178 #endif
180         plugin_init_all ();
182         return (0);
183 } /* int do_init () */
186 static int do_loop (void)
188         int step;
190         struct timeval tv_now;
191         struct timeval tv_next;
192         struct timespec ts_wait;
194         step = atoi (COLLECTD_STEP);
195         if (step <= 0)
196                 step = 10;
198         while (loop == 0)
199         {
200                 if (gettimeofday (&tv_next, NULL) < 0)
201                 {
202                         syslog (LOG_ERR, "gettimeofday failed: %s", strerror (errno));
203                         return (-1);
204                 }
205                 tv_next.tv_sec += step;
207 #if HAVE_LIBKSTAT
208                 update_kstat ();
209 #endif
210                 /* `curtime' is used by many (all?) plugins as the
211                  * data-sample-time passed to RRDTool */
212                 curtime = time (NULL);
214                 /* Issue all plugins */
215                 plugin_read_all (&loop);
217                 if (gettimeofday (&tv_now, NULL) < 0)
218                 {
219                         syslog (LOG_ERR, "gettimeofday failed: %s",
220                                         strerror (errno));
221                         return (-1);
222                 }
224                 if (timeval_sub_timespec (&tv_next, &tv_now, &ts_wait) != 0)
225                 {
226                         syslog (LOG_WARNING, "Not sleeping because "
227                                         "`timeval_sub_timespec' returned "
228                                         "non-zero!");
229                         continue;
230                 }
232                 while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1))
233                 {
234                         if (errno != EINTR)
235                         {
236                                 syslog (LOG_ERR, "nanosleep failed: %s", strerror (errno));
237                                 return (-1);
238                         }
239                 }
240         } /* while (loop == 0) */
242         DBG ("return (0);");
243         return (0);
244 } /* int do_loop */
246 static int do_shutdown (void)
248         plugin_shutdown_all ();
249         return (0);
250 } /* int do_shutdown */
252 #if COLLECT_DAEMON
253 static int pidfile_create (void)
255         FILE *fh;
256         const char *file = global_option_get ("PIDFile");
258         if ((fh = fopen (file, "w")) == NULL)
259         {
260                 syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
261                 return (1);
262         }
264         fprintf (fh, "%i\n", (int) getpid ());
265         fclose(fh);
267         return (0);
268 } /* static int pidfile_create (const char *file) */
270 static int pidfile_remove (void)
272         const char *file = global_option_get ("PIDFile");
274         DBG ("unlink (%s)", (file != NULL) ? file : "<null>");
275         return (unlink (file));
276 } /* static int pidfile_remove (const char *file) */
277 #endif /* COLLECT_DAEMON */
279 int main (int argc, char **argv)
281         struct sigaction sigIntAction;
282         struct sigaction sigTermAction;
283         char *configfile = CONFIGFILE;
284         const char *datadir;
285 #if COLLECT_DAEMON
286         struct sigaction sigChldAction;
287         pid_t pid;
288         int daemonize    = 1;
289 #endif
290 #if COLLECT_DEBUG
291         const char *logfile;
292 #endif
294         /* open syslog */
295         openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
297         /* read options */
298         while (1)
299         {
300                 int c;
302                 c = getopt (argc, argv, "hC:"
303 #if COLLECT_DAEMON
304                                 "fP:"
305 #endif
306                 );
308                 if (c == -1)
309                         break;
311                 switch (c)
312                 {
313                         case 'C':
314                                 configfile = optarg;
315                                 break;
316 #if COLLECT_DAEMON
317                         case 'P':
318                                 global_option_set ("PIDFile", optarg);
319                                 break;
320                         case 'f':
321                                 daemonize = 0;
322                                 break;
323 #endif /* COLLECT_DAEMON */
324                         case 'h':
325                         default:
326                                 exit_usage (argv[0]);
327                 } /* switch (c) */
328         } /* while (1) */
330 #if COLLECT_DEBUG
331         if ((logfile = global_option_get ("LogFile")) != NULL)
332                 DBG_STARTFILE (logfile, "Debug file opened.");
333 #endif
335         /*
336          * Read options from the config file, the environment and the command
337          * line (in that order, with later options overwriting previous ones in
338          * general).
339          * Also, this will automatically load modules.
340          */
341         if (cf_read (configfile))
342         {
343                 fprintf (stderr, "Error: Reading the config file failed!\n"
344                                 "Read the syslog for details.\n");
345                 return (1);
346         }
348         /*
349          * Change directory. We do this _after_ reading the config and loading
350          * modules to relative paths work as expected.
351          */
352         if ((datadir = global_option_get ("BaseDir")) == NULL)
353         {
354                 fprintf (stderr, "Don't have a datadir to use. This should not happen. Ever.");
355                 return (1);
356         }
357         else if (change_basedir (datadir))
358         {
359                 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", datadir);
360                 return (1);
361         }
363 #if COLLECT_DAEMON
364         /*
365          * fork off child
366          */
367         memset (&sigChldAction, '\0', sizeof (sigChldAction));
368         sigChldAction.sa_handler = SIG_IGN;
369         sigaction (SIGCHLD, &sigChldAction, NULL);
371         if (daemonize)
372         {
373                 if ((pid = fork ()) == -1)
374                 {
375                         /* error */
376                         fprintf (stderr, "fork: %s", strerror (errno));
377                         return (1);
378                 }
379                 else if (pid != 0)
380                 {
381                         /* parent */
382                         /* printf ("Running (PID %i)\n", pid); */
383                         return (0);
384                 }
386                 /* Detach from session */
387                 setsid ();
389                 /* Write pidfile */
390                 if (pidfile_create ())
391                         exit (2);
393                 /* close standard descriptors */
394                 close (2);
395                 close (1);
396                 close (0);
398                 if (open ("/dev/null", O_RDWR) != 0)
399                 {
400                         syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
401                         return (1);
402                 }
403                 if (dup (0) != 1)
404                 {
405                         syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
406                         return (1);
407                 }
408                 if (dup (0) != 2)
409                 {
410                         syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
411                         return (1);
412                 }
413         } /* if (daemonize) */
414 #endif /* COLLECT_DAEMON */
416         /*
417          * install signal handlers
418          */
419         memset (&sigIntAction, '\0', sizeof (sigIntAction));
420         sigIntAction.sa_handler = sigIntHandler;
421         sigaction (SIGINT, &sigIntAction, NULL);
423         memset (&sigTermAction, '\0', sizeof (sigTermAction));
424         sigTermAction.sa_handler = sigTermHandler;
425         sigaction (SIGTERM, &sigTermAction, NULL);
427         /*
428          * run the actual loops
429          */
430         do_init ();
431         do_loop ();
432         do_shutdown ();
434 #if COLLECT_DEBUG
435         if (logfile != NULL)
436                 DBG_STOPFILE("debug file closed.");
437 #endif
439         /* close syslog */
440         syslog (LOG_INFO, "Exiting normally");
441         closelog ();
443 #if COLLECT_DAEMON
444         if (daemonize)
445                 pidfile_remove ();
446 #endif /* COLLECT_DAEMON */
448         return (0);
449 } /* int main */