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 **/
24 #include "collectd.h"
25 #include "common.h"
26 #include "utils_debug.h"
28 #include "multicast.h"
29 #include "plugin.h"
30 #include "configfile.h"
32 #include "ping.h"
34 static int loop = 0;
36 #if HAVE_LIBKSTAT
37 kstat_ctl_t *kc;
38 #endif /* HAVE_LIBKSTAT */
40 #if COLLECT_PING
41 char *pinghosts[MAX_PINGHOSTS];
42 int num_pinghosts = 0;
43 #endif
45 /*
46 * exported variables
47 */
48 time_t curtime;
50 #if HAVE_LIBRRD
51 int operating_mode;
52 #endif
54 static void sigIntHandler (int signal)
55 {
56 loop++;
57 }
59 static void sigTermHandler (int signal)
60 {
61 loop++;
62 }
64 static int change_basedir (char *dir)
65 {
66 int dirlen = strlen (dir);
68 while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
69 dir[--dirlen] = '\0';
71 if (dirlen <= 0)
72 return (-1);
74 if (chdir (dir) == -1)
75 {
76 if (errno == ENOENT)
77 {
78 if (mkdir (dir, 0755) == -1)
79 {
80 syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
81 return (-1);
82 }
83 else if (chdir (dir) == -1)
84 {
85 syslog (LOG_ERR, "chdir (%s): %s", dir, strerror (errno));
86 return (-1);
87 }
88 }
89 else
90 {
91 syslog (LOG_ERR, "chdir: %s", strerror (errno));
92 return (-1);
93 }
94 }
96 return (0);
97 } /* static int change_basedir (char *dir) */
99 #if HAVE_LIBKSTAT
100 static void update_kstat (void)
101 {
102 if (kc == NULL)
103 {
104 if ((kc = kstat_open ()) == NULL)
105 syslog (LOG_ERR, "Unable to open kstat control structure");
106 }
107 else
108 {
109 kid_t kid;
110 kid = kstat_chain_update (kc);
111 if (kid > 0)
112 {
113 syslog (LOG_INFO, "kstat chain has been updated");
114 plugin_init_all ();
115 }
116 else if (kid < 0)
117 syslog (LOG_ERR, "kstat chain update failed");
118 /* else: everything works as expected */
119 }
121 return;
122 } /* static void update_kstat (void) */
123 #endif /* HAVE_LIBKSTAT */
125 static int start_client (void)
126 {
127 int sleepingtime;
129 #if HAVE_LIBKSTAT
130 kc = NULL;
131 update_kstat ();
132 #endif
134 #if HAVE_LIBSTATGRAB
135 if (sg_init ())
136 {
137 syslog (LOG_ERR, "sg_init: %s", sg_str_error (sg_get_error ()));
138 return (-1);
139 }
141 if (sg_drop_privileges ())
142 {
143 syslog (LOG_ERR, "sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
144 return (-1);
145 }
146 #endif
148 plugin_init_all ();
150 while (loop == 0)
151 {
152 curtime = time (NULL);
153 #if HAVE_LIBKSTAT
154 update_kstat ();
155 #endif
156 plugin_read_all ();
158 sleepingtime = 10;
159 while (sleepingtime != 0)
160 {
161 if (loop != 0)
162 break;
163 sleepingtime = sleep (sleepingtime);
164 }
165 }
167 return (0);
168 } /* static int start_client (void) */
170 #if HAVE_LIBRRD
171 static int start_server (void)
172 {
173 char *host;
174 char *type;
175 char *instance;
176 char *values;
178 while (loop == 0)
179 {
180 if (multicast_receive (&host, &type, &instance, &values) == 0)
181 plugin_write (host, type, instance, values);
183 if (host != NULL) free (host); host = NULL;
184 if (type != NULL) free (type); type = NULL;
185 if (instance != NULL) free (instance); instance = NULL;
186 if (values != NULL) free (values); values = NULL;
187 }
189 return (0);
190 } /* static int start_server (void) */
191 #endif /* HAVE_LIBRRD */
193 #if COLLECT_DAEMON
194 static int pidfile_create (const char *file)
195 {
196 FILE *fh;
198 if (file == NULL)
199 file = PIDFILE;
201 if ((fh = fopen (file, "w")) == NULL)
202 {
203 syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
204 return (1);
205 }
207 fprintf (fh, "%d\n", getpid());
208 fclose(fh);
210 return (0);
211 } /* static int pidfile_create (const char *file) */
212 #endif /* COLLECT_DAEMON */
214 #if COLLECT_DAEMON
215 static int pidfile_remove (const char *file)
216 {
217 if (file == NULL) {
218 file = PIDFILE;
219 }
220 return (unlink (file));
221 } /* static int pidfile_remove (const char *file) */
222 #endif /* COLLECT_DAEMON */
224 int main (int argc, char **argv)
225 {
226 #if COLLECT_DAEMON
227 struct sigaction sigChldAction;
228 #endif
229 struct sigaction sigIntAction;
230 struct sigaction sigTermAction;
231 char *datadir;
232 #if COLLECT_DAEMON
233 char *pidfile = PIDFILE;
234 pid_t pid;
235 int daemonize = 1;
236 #endif
237 #if COLLECT_DEBUG
238 char *logfile = LOGFILE;
239 #endif
241 #if HAVE_LIBRRD
242 operating_mode = MODE_LOCAL;
243 #endif
245 /* open syslog */
246 openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
248 DBG_STARTFILE(logfile, "Debug file opened.");
250 /*
251 * Read options from the config file, the environment and the command
252 * line (in that order, with later options overwriting previous ones in
253 * general).
254 * Also, this will automatically load modules.
255 */
256 if (cf_read (argc, argv, CONFIGFILE))
257 {
258 fprintf (stderr, "Error: Reading the config file failed!\n"
259 "Read the syslog for details.\n");
260 return (1);
261 }
263 /*
264 * Change directory. We do this _after_ reading the config and loading
265 * modules to relative paths work as expected.
266 */
267 if ((datadir = cf_get_mode_option ("DataDir")) == NULL)
268 {
269 fprintf (stderr, "Don't have a datadir to use. This should not happen. Ever.");
270 return (1);
271 }
272 if (change_basedir (datadir))
273 {
274 fprintf (stderr, "Error: Unable to change to directory `%s'.\n", datadir);
275 return (1);
276 }
278 #if COLLECT_DAEMON
279 /*
280 * fork off child
281 */
282 sigChldAction.sa_handler = SIG_IGN;
283 sigaction (SIGCHLD, &sigChldAction, NULL);
285 if ((pidfile = cf_get_mode_option ("PIDFile")) == NULL)
286 {
287 fprintf (stderr, "Cannot obtain pidfile. This shoud not happen. Ever.");
288 return (1);
289 }
291 if (daemonize)
292 {
293 if ((pid = fork ()) == -1)
294 {
295 /* error */
296 fprintf (stderr, "fork: %s", strerror (errno));
297 return (1);
298 }
299 else if (pid != 0)
300 {
301 /* parent */
302 /* printf ("Running (PID %i)\n", pid); */
303 return (0);
304 }
306 /* Detach from session */
307 setsid ();
309 /* Write pidfile */
310 if (pidfile_create (pidfile))
311 exit (2);
313 /* close standard descriptors */
314 close (2);
315 close (1);
316 close (0);
318 if (open ("/dev/null", O_RDWR) != 0)
319 {
320 syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
321 return (1);
322 }
323 if (dup (0) != 1)
324 {
325 syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
326 return (1);
327 }
328 if (dup (0) != 2)
329 {
330 syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
331 return (1);
332 }
333 } /* if (daemonize) */
334 #endif /* COLLECT_DAEMON */
336 /*
337 * install signal handlers
338 */
339 sigIntAction.sa_handler = sigIntHandler;
340 sigaction (SIGINT, &sigIntAction, NULL);
342 sigTermAction.sa_handler = sigTermHandler;
343 sigaction (SIGTERM, &sigTermAction, NULL);
345 /*
346 * run the actual loops
347 */
348 #if HAVE_LIBRRD
349 if (operating_mode == MODE_SERVER)
350 start_server ();
351 else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL) */
352 #endif
353 start_client ();
355 DBG_STOPFILE("debug file closed.");
357 /* close syslog */
358 syslog (LOG_INFO, "Exiting normally");
359 closelog ();
361 #if COLLECT_DAEMON
362 if (daemonize)
363 pidfile_remove (pidfile);
364 #endif /* COLLECT_DAEMON */
366 return (0);
367 } /* int main (int argc, char **argv) */