Took a closer `look' at last nights changes..
[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
30 #include "ping.h"
31
32 static int loop = 0;
33
34 #if HAVE_LIBKSTAT
35 kstat_ctl_t *kc;
36 #endif /* HAVE_LIBKSTAT */
37
38 #if COLLECT_PING
39 char *pinghosts[MAX_PINGHOSTS];
40 int   num_pinghosts = 0;
41 #endif
42
43 /*
44  * exported variables
45  */
46 time_t curtime;
47
48 #if HAVE_LIBRRD
49 int operating_mode;
50 #endif
51
52 static void sigIntHandler (int signal)
53 {
54         loop++;
55 }
56
57 static int change_basedir (char *dir)
58 {
59         int dirlen = strlen (dir);
60         
61         while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
62                 dir[--dirlen] = '\0';
63
64         if (dirlen <= 0)
65                 return (-1);
66
67         if (chdir (dir) == -1)
68         {
69                 if (errno == ENOENT)
70                 {
71                         if (mkdir (dir, 0755) == -1)
72                         {
73                                 syslog (LOG_ERR, "mkdir: %s", strerror (errno));
74                                 return (-1);
75                         }
76                         else if (chdir (dir) == -1)
77                         {
78                                 syslog (LOG_ERR, "chdir: %s", strerror (errno));
79                                 return (-1);
80                         }
81                 }
82                 else
83                 {
84                         syslog (LOG_ERR, "chdir: %s", strerror (errno));
85                         return (-1);
86                 }
87         }
88
89         return (0);
90 } /* static int change_basedir (char *dir) */
91
92 #if HAVE_LIBKSTAT
93 static void update_kstat (void)
94 {
95         if (kc == NULL)
96         {
97                 if ((kc = kstat_open ()) == NULL)
98                         syslog (LOG_ERR, "Unable to open kstat control structure");
99         }
100         else
101         {
102                 kid_t kid;
103                 kid = kstat_chain_update (kc);
104                 if (kid > 0)
105                 {
106                         syslog (LOG_INFO, "kstat chain has been updated");
107                         plugin_init_all ();
108                 }
109                 else if (kid < 0)
110                         syslog (LOG_ERR, "kstat chain update failed");
111                 /* else: everything works as expected */
112         }
113
114         return;
115 } /* static void update_kstat (void) */
116 #endif /* HAVE_LIBKSTAT */
117
118 static void exit_usage (char *name)
119 {
120         printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
121                         
122                         "Available options:\n"
123                         "  General:\n"
124                         "    -C <file>       Configuration file.\n"
125                         "                    Default: "CONFIGFILE"\n"
126 #if COLLECT_DAEMON
127                         "    -P <file>       PID file.\n"
128                         "                    Default: "PIDFILE"\n"
129 #endif
130                         "    -M <dir>        Module/Plugin directory.\n"
131                         "                    Default: "PLUGINDIR"\n"
132                         "    -D <dir>        Data storage directory.\n"
133                         "                    Default: "PKGLOCALSTATEDIR"\n"
134 #if COLLECT_DEBUG
135                         "    -L <file>       Log file.\n"
136                         "                    Default: "LOGFILE"\n"
137 #endif
138 #if COLLECT_DAEMON
139                         "    -f              Don't fork to the background.\n"
140 #endif
141 #if HAVE_LIBRRD
142                         "    -l              Start in local mode (no network).\n"
143                         "    -c              Start in client (sender) mode.\n"
144                         "    -s              Start in server (listener) mode.\n"
145 #endif /* HAVE_LIBRRD */
146 #if COLLECT_PING
147                         "  Ping:\n"
148                         "    -p <host>       Host to ping periodically, may be repeated to ping\n"
149                         "                    more than one host.\n"
150 #endif /* COLLECT_PING */
151                         "\n"PACKAGE" "VERSION", http://verplant.org/collectd/\n"
152                         "by Florian octo Forster <octo@verplant.org>\n"
153                         "for contributions see `AUTHORS'\n");
154         exit (0);
155 } /* static void exit_usage (char *name) */
156
157 static int start_client (void)
158 {
159         int sleepingtime;
160
161 #if HAVE_LIBKSTAT
162         kc = NULL;
163         update_kstat ();
164 #endif
165
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         }
172
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
179
180         plugin_init_all ();
181
182         while (loop == 0)
183         {
184                 curtime = time (NULL);
185 #if HAVE_LIBKSTAT
186                 update_kstat ();
187 #endif
188                 plugin_read_all ();
189
190                 sleepingtime = 10;
191                 while (sleepingtime != 0)
192                 {
193                         if (loop != 0)
194                                 break;
195                         sleepingtime = sleep (sleepingtime);
196                 }
197         }
198
199         return (0);
200 } /* static int start_client (void) */
201
202 #if HAVE_LIBRRD
203 static int start_server (void)
204 {
205         char *host;
206         char *type;
207         char *instance;
208         char *values;
209
210         while (loop == 0)
211         {
212                 if (multicast_receive (&host, &type, &instance, &values) == 0)
213                         plugin_write (host, type, instance, values);
214
215                 if (host     != NULL) free (host);     host     = NULL;
216                 if (type     != NULL) free (type);     type     = NULL;
217                 if (instance != NULL) free (instance); instance = NULL;
218                 if (values   != NULL) free (values);   values   = NULL;
219         }
220         
221         return (0);
222 } /* static int start_server (void) */
223 #endif /* HAVE_LIBRRD */
224
225 #if COLLECT_DAEMON
226 static int pidfile_create (const char *file)
227 {
228         FILE *fh;
229
230         if (file == NULL)
231                 file = PIDFILE;
232
233         if ((fh = fopen (file, "w")) == NULL)
234         {
235                 syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
236                 return (1);
237         }
238
239         fprintf (fh, "%d\n", getpid());
240         fclose(fh);
241
242         return (0);
243 } /* static int pidfile_create (const char *file) */
244 #endif /* COLLECT_DAEMON */
245
246 #if COLLECT_DAEMON
247 static int pidfile_remove (const char *file)
248 {
249         if (file == NULL) {
250                 file = PIDFILE;
251         }
252         return (unlink (file));
253 } /* static int pidfile_remove (const char *file) */
254 #endif /* COLLECT_DAEMON */
255
256 int main (int argc, char **argv)
257 {
258         struct sigaction sigIntAction, sigChldAction;
259         char *configfile = CONFIGFILE;
260         char *plugindir  = PLUGINDIR;
261         char *datadir    = PKGLOCALSTATEDIR;
262 #if COLLECT_DAEMON
263         char *pidfile    = PIDFILE;
264         pid_t pid;
265         int daemonize    = 1;
266 #endif
267 #if COLLECT_DEBUG
268         char *logfile    = LOGFILE;
269 #endif
270
271 #if HAVE_LIBRRD
272         operating_mode = MODE_LOCAL;
273 #endif
274
275         /* open syslog */
276         openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
277
278         /* read options */
279         while (1)
280         {
281                 int c;
282
283                 c = getopt (argc, argv, "C:M:D:h"
284 #if COLLECT_DAEMON
285                                 "fP:"
286 #endif
287 #if COLLECT_DEBUG
288                                 "L:"
289 #endif
290 #if HAVE_LIBRRD
291                                 "csl"
292 #endif /* HAVE_LIBRRD */
293 #if COLLECT_PING
294                                 "p:"
295 #endif /* COLLECT_PING */
296                 );
297
298                 if (c == -1)
299                         break;
300
301                 switch (c)
302                 {
303 #if HAVE_LIBRRD
304                         case 'c':
305                                 operating_mode = MODE_CLIENT;
306                                 break;
307
308                         case 's':
309                                 operating_mode = MODE_SERVER;
310                                 break;
311
312                         case 'l':
313                                 operating_mode = MODE_LOCAL;
314                                 break;
315 #endif /* HAVE_LIBRRD */
316                         case 'C':
317                                 configfile = optarg;
318                                 break;
319 #if COLLECT_DAEMON
320                         case 'P':
321                                 pidfile = optarg;
322                                 break;
323                         case 'f':
324                                 daemonize = 0;
325                                 break;
326 #endif /* COLLECT_DAEMON */
327                         case 'M':
328                                 plugindir = optarg;
329                                 break;
330                         case 'D':
331                                 datadir = optarg;
332                                 break;
333 #if COLLECT_DEBUG
334                         case 'L':
335                                 logfile = optarg;
336                                 break;
337 #endif
338 #if COLLECT_PING
339                         case 'p':
340                                 if (num_pinghosts < MAX_PINGHOSTS)
341                                         pinghosts[num_pinghosts++] = optarg;
342                                 else
343                                         fprintf (stderr, "Maximum of %i ping hosts reached.\n", MAX_PINGHOSTS);
344                                 break;
345 #endif /* COLLECT_PING */
346                         case 'h':
347                         default:
348                                 exit_usage (argv[0]);
349                 } /* switch (c) */
350         } /* while (1) */
351
352         DBG_STARTFILE(logfile, "debug file opened.");
353
354         /*
355          * Load plugins and change to output directory
356          * Loading plugins is done first so relative paths work as expected..
357          */
358         if (plugin_load_all (plugindir) < 1)
359         {
360                 fprintf (stderr, "Error: No plugins found.\n");
361                 return (1);
362         }
363
364         if (change_basedir (datadir))
365         {
366                 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", datadir);
367                 return (1);
368         }
369
370         /*
371          * install signal handlers
372          */
373         sigIntAction.sa_handler = sigIntHandler;
374         sigaction (SIGINT, &sigIntAction, NULL);
375
376         sigChldAction.sa_handler = SIG_IGN;
377         sigaction (SIGCHLD, &sigChldAction, NULL);
378
379         /*
380          * fork off child
381          */
382 #if COLLECT_DAEMON
383         if (daemonize)
384         {
385                 if ((pid = fork ()) == -1)
386                 {
387                         /* error */
388                         fprintf (stderr, "fork: %s", strerror (errno));
389                         return (1);
390                 }
391                 else if (pid != 0)
392                 {
393                         /* parent */
394                         /* printf ("Running (PID %i)\n", pid); */
395                         return (0);
396                 }
397
398                 /* Detach from session */
399                 setsid ();
400
401                 /* Write pidfile */
402                 if (pidfile_create (pidfile))
403                         exit (2);
404
405                 /* close standard descriptors */
406                 close (2);
407                 close (1);
408                 close (0);
409
410                 if (open ("/dev/null", O_RDWR) != 0)
411                 {
412                         syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
413                         return (1);
414                 }
415                 if (dup (0) != 1)
416                 {
417                         syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
418                         return (1);
419                 }
420                 if (dup (0) != 2)
421                 {
422                         syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
423                         return (1);
424                 }
425         } /* if (daemonize) */
426 #endif /* COLLECT_DAEMON */
427
428         /*
429          * run the actual loops
430          */
431 #if HAVE_LIBRRD
432         if (operating_mode == MODE_SERVER)
433                 start_server ();
434         else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL) */
435 #endif
436                 start_client ();
437
438         DBG_STOPFILE("debug file closed.");
439
440         /* close syslog */
441         syslog (LOG_INFO, "Exiting normally");
442         closelog ();
443
444 #if COLLECT_DAEMON
445         if (daemonize)
446                 pidfile_remove(pidfile);
447 #endif /* COLLECT_DAEMON */
448
449         return (0);
450 } /* int main (int argc, char **argv) */