RRDs and PID file are now underneath `localstatedir'
[collectd.git] / src / collectd.c
1 #include "collectd.h"
2
3 #include "multicast.h"
4 #include "plugin.h"
5
6 #include "ping.h"
7
8 static int loop = 0;
9
10 #ifdef HAVE_LIBKSTAT
11 kstat_ctl_t *kc;
12 #endif /* HAVE_LIBKSTAT */
13
14 #if COLLECT_PING
15 char *pinghosts[MAX_PINGHOSTS];
16 int   num_pinghosts = 0;
17 #endif
18
19 /*
20  * exported variables
21  */
22 time_t curtime;
23
24 #ifdef HAVE_LIBRRD
25 int operating_mode;
26 #endif
27
28 void sigIntHandler (int signal)
29 {
30         loop++;
31 }
32
33 int change_basedir (char *dir)
34 {
35         int dirlen = strlen (dir);
36         
37         while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
38                 dir[--dirlen] = '\0';
39
40         if (dirlen <= 0)
41                 return (-1);
42
43         if (chdir (dir) == -1)
44         {
45                 if (errno == ENOENT)
46                 {
47                         if (mkdir (dir, 0755) == -1)
48                         {
49                                 syslog (LOG_ERR, "mkdir: %s", strerror (errno));
50                                 return (-1);
51                         }
52                         else if (chdir (dir) == -1)
53                         {
54                                 syslog (LOG_ERR, "chdir: %s", strerror (errno));
55                                 return (-1);
56                         }
57                 }
58                 else
59                 {
60                         syslog (LOG_ERR, "chdir: %s", strerror (errno));
61                         return (-1);
62                 }
63         }
64
65         return (0);
66 }
67
68 #ifdef HAVE_LIBKSTAT
69 void update_kstat (void)
70 {
71         if (kc == NULL)
72         {
73                 if ((kc = kstat_open ()) == NULL)
74                         syslog (LOG_ERR, "Unable to open kstat control structure");
75         }
76         else
77         {
78                 kid_t kid;
79                 kid = kstat_chain_update (kc);
80                 if (kid > 0)
81                 {
82                         syslog (LOG_INFO, "kstat chain has been updated");
83                         plugin_init_all ();
84                 }
85                 else if (kid < 0)
86                         syslog (LOG_ERR, "kstat chain update failed");
87                 /* else: everything works as expected */
88         }
89
90         return;
91 }
92 #endif /* HAVE_LIBKSTAT */
93
94 void exit_usage (char *name)
95 {
96         printf ("Usage: %s [OPTIONS]\n\n"
97                         
98                         "Available options:\n"
99                         "  General:\n"
100                         "    -d <dir>        Base directory to use.\n"
101                         "                    Default: %s\n"
102                         "    -P <dir>        Set the plugin-directory\n"
103                         "                    Default: %s\n"
104                         "    -f              Don't fork to the background\n"
105 #ifdef HAVE_LIBRRD
106                         "    -l              Start in local mode (no network)\n"
107                         "    -c              Start in client (sender) mode\n"
108                         "    -s              Start in server (listener) mode\n"
109 #endif /* HAVE_LIBRRD */
110 #if COLLECT_PING
111                         "  Ping:\n"
112                         "    -p <host>       Host to ping periodically, may be repeated to ping\n"
113                         "                    more than one host.\n"
114 #endif /* COLLECT_PING */
115                         "\n%s %s, http://verplant.org/collectd/\n"
116                         "by Florian octo Forster <octo@verplant.org>\n"
117                         "for contributions see `AUTHORS'\n",
118                         PACKAGE, DATADIR, PLUGINDIR, PACKAGE, VERSION);
119         exit (0);
120 }
121
122 int start_client (void)
123 {
124         int sleepingtime;
125
126 #ifdef HAVE_LIBKSTAT
127         kc = NULL;
128         update_kstat ();
129 #endif
130
131 #ifdef HAVE_LIBSTATGRAB
132         if (sg_init ())
133         {
134                 syslog (LOG_ERR, "sg_init: %s", sg_str_error (sg_get_error ()));
135                 return (-1);
136         }
137
138         if (sg_drop_privileges ())
139         {
140                 syslog (LOG_ERR, "sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
141                 return (-1);
142         }
143 #endif
144
145         plugin_init_all ();
146
147         while (loop == 0)
148         {
149                 curtime = time (NULL);
150 #ifdef HAVE_LIBKSTAT
151                 update_kstat ();
152 #endif
153                 plugin_read_all ();
154
155                 sleepingtime = 10;
156                 while (sleepingtime != 0)
157                 {
158                         if (loop != 0)
159                                 break;
160                         sleepingtime = sleep (sleepingtime);
161                 }
162         }
163
164         return (0);
165 }
166
167 #ifdef HAVE_LIBRRD
168 int start_server (void)
169 {
170         char *host;
171         char *type;
172         char *instance;
173         char *values;
174
175         while (loop == 0)
176         {
177                 if (multicast_receive (&host, &type, &instance, &values) == 0)
178                         plugin_write (host, type, instance, values);
179
180                 if (host     != NULL) free (host);     host     = NULL;
181                 if (type     != NULL) free (type);     type     = NULL;
182                 if (instance != NULL) free (instance); instance = NULL;
183                 if (values   != NULL) free (values);   values   = NULL;
184         }
185         
186         return (0);
187 }
188 #endif /* HAVE_LIBRRD */
189
190 int pidfile_create (void)
191 {
192         FILE *fh;
193
194         if ((fh = fopen (PIDFILE, "w")) == NULL)
195         {
196                 syslog (LOG_ERR, "fopen (pidfile): %s", strerror (errno));
197                 return (1);
198         }
199
200         fprintf (fh, "%d\n", getpid());
201         fclose(fh);
202
203         return (0);
204 }
205
206 int pidfile_remove (void)
207 {
208       return (unlink (PIDFILE));
209 }
210
211 int main (int argc, char **argv)
212 {
213         struct sigaction sigIntAction, sigChldAction;
214         pid_t pid;
215
216         char *plugindir = NULL;
217         char *basedir = DATADIR;
218
219         int daemonize = 1;
220
221 #ifdef HAVE_LIBRRD
222         operating_mode = MODE_LOCAL;
223 #endif
224         
225         /*
226          * open syslog
227          */
228         openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
229
230         /*
231          * read options
232          */
233         while (1)
234         {
235                 int c;
236
237                 c = getopt (argc, argv, "d:fP:h"
238 #if HAVE_LIBRRD
239                                 "csl"
240 #endif /* HAVE_LIBRRD */
241 #if COLLECT_PING
242                                 "p:"
243 #endif /* COLLECT_PING */
244                 );
245
246                 if (c == -1)
247                         break;
248
249                 switch (c)
250                 {
251 #ifdef HAVE_LIBRRD
252                         case 'c':
253                                 operating_mode = MODE_CLIENT;
254                                 break;
255
256                         case 's':
257                                 operating_mode = MODE_SERVER;
258                                 break;
259
260                         case 'l':
261                                 operating_mode = MODE_LOCAL;
262                                 break;
263 #endif /* HAVE_LIBRRD */
264                         case 'd':
265                                 basedir = optarg;
266                                 break;
267                         case 'f':
268                                 daemonize = 0;
269                                 break;
270 #if COLLECT_PING
271                         case 'p':
272                                 if (num_pinghosts < MAX_PINGHOSTS)
273                                         pinghosts[num_pinghosts++] = optarg;
274                                 else
275                                         fprintf (stderr, "Maximum of %i ping hosts reached.\n", MAX_PINGHOSTS);
276                                 break;
277 #endif /* COLLECT_PING */
278                         case 'P':
279                                 plugindir = optarg;
280                                 break;
281
282                         case 'h':
283                         default:
284                                 exit_usage (argv[0]);
285                 }
286                                 
287         }
288
289         /*
290          * Load plugins and change to output directory
291          * Loading plugins is done first so relative paths work as expected..
292          */
293         if (plugin_load_all (plugindir) < 1)
294         {
295                 fprintf (stderr, "Error: No plugins found.\n");
296                 return (1);
297         }
298
299         if (change_basedir (basedir))
300         {
301                 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", basedir);
302                 return (1);
303         }
304
305         /*
306          * install signal handlers
307          */
308         sigIntAction.sa_handler = sigIntHandler;
309         sigaction (SIGINT, &sigIntAction, NULL);
310
311         sigChldAction.sa_handler = SIG_IGN;
312         sigaction (SIGCHLD, &sigChldAction, NULL);
313
314         /*
315          * fork off child
316          */
317 #if DEBUG == 0
318         if (daemonize)
319         {
320                 if ((pid = fork ()) == -1)
321                 {
322                         /* error */
323                         fprintf (stderr, "fork: %s", strerror (errno));
324                         return (1);
325                 }
326                 else if (pid != 0)
327                 {
328                         /* parent */
329                         /* printf ("Running (PID %i)\n", pid); */
330                         return (0);
331                 }
332
333                 /* Detach from session */
334                 setsid ();
335
336                 /* Write pidfile */
337                 if (pidfile_create ())
338                         exit (2);
339
340                 /* close standard descriptors */
341                 close (2);
342                 close (1);
343                 close (0);
344
345                 if (open ("/dev/null", O_RDWR) != 0)
346                 {
347                         syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
348                         return (1);
349                 }
350                 if (dup (0) != 1)
351                 {
352                         syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
353                         return (1);
354                 }
355                 if (dup (0) != 2)
356                 {
357                         syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
358                         return (1);
359                 }
360         } /* if (daemonize) */
361 #endif
362
363         /*
364          * run the actual loops
365          */
366 #ifdef HAVE_LIBRRD
367         if (operating_mode == MODE_SERVER)
368                 start_server ();
369         else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL) */
370 #endif
371                 start_client ();
372
373         /*
374          * close syslog
375          */
376         syslog (LOG_INFO, "Exiting normally");
377         closelog ();
378
379         if (daemonize)
380                 pidfile_remove();
381
382         return (0);
383 }