Merged branch configfile to trunk
[collectd.git] / src / collectd.c
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  **/
23
24 #include "common.h"
25 #include "utils_debug.h"
26
27 #include "multicast.h"
28 #include "plugin.h"
29 #include "configfile.h"
30
31 #include "ping.h"
32
33 static int loop = 0;
34
35 #if HAVE_LIBKSTAT
36 kstat_ctl_t *kc;
37 #endif /* HAVE_LIBKSTAT */
38
39 #if COLLECT_PING
40 char *pinghosts[MAX_PINGHOSTS];
41 int   num_pinghosts = 0;
42 #endif
43
44 /*
45  * exported variables
46  */
47 time_t curtime;
48
49 #if HAVE_LIBRRD
50 int operating_mode;
51 #endif
52
53 static void sigIntHandler (int signal)
54 {
55         loop++;
56 }
57
58 static int change_basedir (char *dir)
59 {
60         int dirlen = strlen (dir);
61         
62         while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
63                 dir[--dirlen] = '\0';
64
65         if (dirlen <= 0)
66                 return (-1);
67
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         }
89
90         return (0);
91 } /* static int change_basedir (char *dir) */
92
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         }
114
115         return;
116 } /* static void update_kstat (void) */
117 #endif /* HAVE_LIBKSTAT */
118
119 static void exit_usage (char *name)
120 {
121         printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
122                         
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) */
157
158 static int start_client (void)
159 {
160         int sleepingtime;
161
162 #if HAVE_LIBKSTAT
163         kc = NULL;
164         update_kstat ();
165 #endif
166
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         }
173
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
180
181         plugin_init_all ();
182
183         while (loop == 0)
184         {
185                 curtime = time (NULL);
186 #if HAVE_LIBKSTAT
187                 update_kstat ();
188 #endif
189                 plugin_read_all ();
190
191                 sleepingtime = 10;
192                 while (sleepingtime != 0)
193                 {
194                         if (loop != 0)
195                                 break;
196                         sleepingtime = sleep (sleepingtime);
197                 }
198         }
199
200         return (0);
201 } /* static int start_client (void) */
202
203 #if HAVE_LIBRRD
204 static int start_server (void)
205 {
206         char *host;
207         char *type;
208         char *instance;
209         char *values;
210
211         while (loop == 0)
212         {
213                 if (multicast_receive (&host, &type, &instance, &values) == 0)
214                         plugin_write (host, type, instance, values);
215
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         }
221         
222         return (0);
223 } /* static int start_server (void) */
224 #endif /* HAVE_LIBRRD */
225
226 #if COLLECT_DAEMON
227 static int pidfile_create (const char *file)
228 {
229         FILE *fh;
230
231         if (file == NULL)
232                 file = PIDFILE;
233
234         if ((fh = fopen (file, "w")) == NULL)
235         {
236                 syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
237                 return (1);
238         }
239
240         fprintf (fh, "%d\n", getpid());
241         fclose(fh);
242
243         return (0);
244 } /* static int pidfile_create (const char *file) */
245 #endif /* COLLECT_DAEMON */
246
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 */
256
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
271
272 #if HAVE_LIBRRD
273         operating_mode = MODE_LOCAL;
274 #endif
275
276         /* open syslog */
277         openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
278
279         /* read options */
280         while (1)
281         {
282                 int c;
283
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                 );
298
299                 if (c == -1)
300                         break;
301
302                 switch (c)
303                 {
304 #if HAVE_LIBRRD
305                         case 'c':
306                                 operating_mode = MODE_CLIENT;
307                                 break;
308
309                         case 's':
310                                 operating_mode = MODE_SERVER;
311                                 break;
312
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) */
352
353         DBG_STARTFILE(logfile, "debug file opened.");
354
355         /*
356          * Read the config file. This will load any modules automagically.
357          */
358         plugin_set_dir (plugindir);
359
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         }
366
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         }
376
377         /*
378          * install signal handlers
379          */
380         sigIntAction.sa_handler = sigIntHandler;
381         sigaction (SIGINT, &sigIntAction, NULL);
382
383         sigChldAction.sa_handler = SIG_IGN;
384         sigaction (SIGCHLD, &sigChldAction, NULL);
385
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                 }
404
405                 /* Detach from session */
406                 setsid ();
407
408                 /* Write pidfile */
409                 if (pidfile_create (pidfile))
410                         exit (2);
411
412                 /* close standard descriptors */
413                 close (2);
414                 close (1);
415                 close (0);
416
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 */
434
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 ();
444
445         DBG_STOPFILE("debug file closed.");
446
447         /* close syslog */
448         syslog (LOG_INFO, "Exiting normally");
449         closelog ();
450
451 #if COLLECT_DAEMON
452         if (daemonize)
453                 pidfile_remove(pidfile);
454 #endif /* COLLECT_DAEMON */
455
456         return (0);
457 } /* int main (int argc, char **argv) */