272b85965d45853548282632b326edf0b0f373b7
[rrdd.git] / src / rrdd.c
1 /**
2  * collectd - src/rrdd.c
3  * Copyright (C) 2008 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  **/
21
22 #define RRDD_DEBUG 1
23
24 #include "rrdd.h"
25 #include <glib-2.0/glib.h>
26 #include <rrd.h>
27
28 #if RRDD_DEBUG
29 # define RRDD_LOG(severity, ...) do { fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0)
30 #else
31 # define RRDD_LOG(severity, ...) syslog ((severity), __VA_ARGS__)
32 #endif
33
34 /*
35  * Types
36  */
37 struct listen_socket_s
38 {
39   int fd;
40   char path[PATH_MAX + 1];
41 };
42 typedef struct listen_socket_s listen_socket_t;
43
44 struct cache_item_s;
45 typedef struct cache_item_s cache_item_t;
46 struct cache_item_s
47 {
48   char *file;
49   char **values;
50   int values_num;
51   time_t last_flush_time;
52 #define CI_FLAGS_IN_TREE  0x01
53 #define CI_FLAGS_IN_QUEUE 0x02
54   int flags;
55
56   cache_item_t *next;
57 };
58
59 /*
60  * Variables
61  */
62 static listen_socket_t *listen_fds = NULL;
63 static size_t listen_fds_num = 0;
64
65 static int do_shutdown = 0;
66
67 static pthread_t queue_thread;
68
69 static pthread_t *connetion_threads = NULL;
70 static pthread_mutex_t connetion_threads_lock = PTHREAD_MUTEX_INITIALIZER;
71 static int connetion_threads_num = 0;
72
73 /* Cache stuff */
74 static GTree          *cache_tree = NULL;
75 static cache_item_t   *cache_queue_head = NULL;
76 static cache_item_t   *cache_queue_tail = NULL;
77 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
78 static pthread_cond_t  cache_cond = PTHREAD_COND_INITIALIZER;
79
80 static int config_write_interval = 300;
81 static int config_flush_interval = 3600;
82
83 static char **config_listen_address_list = NULL;
84 static int config_listen_address_list_len = 0;
85
86 /* 
87  * Functions
88  */
89 static void sig_int_handler (int signal) /* {{{ */
90 {
91   do_shutdown++;
92 } /* }}} void sig_int_handler */
93
94 static void sig_term_handler (int signal) /* {{{ */
95 {
96   do_shutdown++;
97 } /* }}} void sig_term_handler */
98
99 static void *queue_thread_main (void *args) /* {{{ */
100 {
101   pthread_mutex_lock (&cache_lock);
102   while ((do_shutdown == 0) || (cache_queue_head != NULL))
103   {
104     cache_item_t *ci;
105
106     char *file;
107     char **values;
108     int values_num;
109     int status;
110     int i;
111
112     if (cache_queue_head == NULL)
113       pthread_cond_wait (&cache_cond, &cache_lock);
114
115     if (cache_queue_head == NULL)
116       continue;
117
118     ci = cache_queue_head;
119
120     /* copy the relevant parts */
121     file = strdup (ci->file);
122     if (file == NULL)
123     {
124       RRDD_LOG (LOG_ERR, "queue_thread_main: strdup failed.");
125       continue;
126     }
127
128     values = ci->values;
129     values_num = ci->values_num;
130
131     ci->values = NULL;
132     ci->values_num = 0;
133
134     ci->last_flush_time = time (NULL);
135     ci->flags &= ~(CI_FLAGS_IN_QUEUE);
136
137     cache_queue_head = ci->next;
138     if (cache_queue_head == NULL)
139       cache_queue_tail = NULL;
140     ci->next = NULL;
141
142     pthread_mutex_unlock (&cache_lock);
143
144     RRDD_LOG (LOG_DEBUG, "queue_thread_main: rrd_update (%s, %i, %p)",
145         file, values_num, (void *) values);
146
147     status = rrd_update_r (file, NULL, values_num, (void *) values);
148     if (status != 0)
149     {
150       RRDD_LOG (LOG_ERR, "queue_thread_main: "
151           "rrd_update_r failed with status %i.",
152           status);
153     }
154
155     free (file);
156     for (i = 0; i < values_num; i++)
157       free (values[i]);
158
159     pthread_mutex_lock (&cache_lock);
160   } /* while (do_shutdown == 0) */
161   pthread_mutex_unlock (&cache_lock);
162
163   RRDD_LOG (LOG_DEBUG, "queue_thread_main: Exiting.");
164
165   return (NULL);
166 } /* }}} void *queue_thread_main */
167
168 static int handle_request_update (int fd, /* {{{ */
169     char *buffer, int buffer_size)
170 {
171   char *file;
172   char *value;
173   char *buffer_ptr;
174   int values_num = 0;
175   int status;
176
177   time_t now;
178
179   cache_item_t *ci;
180   char answer[4096];
181
182   now = time (NULL);
183
184   RRDD_LOG (LOG_DEBUG, "handle_request_update (%i, %p, %i)",
185       fd, (void *) buffer, buffer_size);
186
187   buffer_ptr = buffer;
188
189   file = buffer_ptr;
190   buffer_ptr += strlen (file) + 1;
191
192   pthread_mutex_lock (&cache_lock);
193
194   ci = g_tree_lookup (cache_tree, file);
195   if (ci == NULL)
196   {
197     ci = (cache_item_t *) malloc (sizeof (cache_item_t));
198     if (ci == NULL)
199     {
200       pthread_mutex_unlock (&cache_lock);
201       RRDD_LOG (LOG_ERR, "handle_request_update: malloc failed.");
202       return (-1);
203     }
204     memset (ci, 0, sizeof (cache_item_t));
205
206     ci->file = strdup (file);
207     if (ci->file == NULL)
208     {
209       pthread_mutex_unlock (&cache_lock);
210       RRDD_LOG (LOG_ERR, "handle_request_update: malloc failed.");
211       free (ci);
212       return (-1);
213     }
214
215     ci->values = NULL;
216     ci->values_num = 0;
217     ci->last_flush_time = now;
218     ci->flags = CI_FLAGS_IN_TREE;
219
220     g_tree_insert (cache_tree, (void *) ci->file, (void *) ci);
221
222     RRDD_LOG (LOG_DEBUG, "handle_request_update: Created new tree node %s.",
223         ci->file);
224   }
225   assert (ci != NULL);
226
227   while (*buffer_ptr != 0)
228   {
229     char **temp;
230
231     value = buffer_ptr;
232     buffer_ptr += strlen (value) + 1;
233
234     temp = (char **) realloc (ci->values,
235         sizeof (char *) * (ci->values_num + 1));
236     if (temp == NULL)
237     {
238       RRDD_LOG (LOG_ERR, "handle_request_update: realloc failed.");
239       continue;
240     }
241     ci->values = temp;
242
243     ci->values[ci->values_num] = strdup (value);
244     if (ci->values[ci->values_num] == NULL)
245     {
246       RRDD_LOG (LOG_ERR, "handle_request_update: strdup failed.");
247       continue;
248     }
249     ci->values_num++;
250
251     values_num++;
252   }
253
254   if (((now - ci->last_flush_time) >= config_write_interval)
255       && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0))
256   {
257     RRDD_LOG (LOG_DEBUG, "handle_request_update: Adding %s to the update queue.",
258         ci->file);
259
260     assert (ci->next == NULL);
261
262     if (cache_queue_tail == NULL)
263       cache_queue_head = ci;
264     else
265       cache_queue_tail->next = ci;
266     cache_queue_tail = ci;
267
268     pthread_cond_signal (&cache_cond);
269   }
270
271   pthread_mutex_unlock (&cache_lock);
272
273   snprintf (answer, sizeof (answer), "0 Enqueued %i value(s)\n", values_num);
274   answer[sizeof (answer) - 1] = 0;
275
276   status = write (fd, answer, sizeof (answer));
277   if (status < 0)
278   {
279     status = errno;
280     RRDD_LOG (LOG_INFO, "handle_request_update: write(2) returned an error.");
281     return (status);
282   }
283
284   return (0);
285 } /* }}} int handle_request_update */
286
287 static int handle_request (int fd) /* {{{ */
288 {
289   char buffer[4096];
290   int buffer_size;
291
292   RRDD_LOG (LOG_DEBUG, "handle_request (%i)", fd);
293
294   buffer_size = read (fd, buffer, sizeof (buffer));
295   if (buffer_size < 1)
296   {
297     RRDD_LOG (LOG_ERR, "handle_request: read(2) failed.");
298     return (-1);
299   }
300   assert (((size_t) buffer_size) <= sizeof (buffer));
301
302   if ((buffer[buffer_size - 2] != 0)
303       || (buffer[buffer_size - 1] != 0))
304   {
305     RRDD_LOG (LOG_INFO, "handle_request: malformed request.");
306     return (-1);
307   }
308
309   /* fields in the buffer a separated by null bytes. */
310   if (strcmp (buffer, "update") == 0)
311   {
312     int offset = strlen ("update") + 1;
313     return (handle_request_update (fd, buffer + offset,
314           buffer_size - offset));
315   }
316   else
317   {
318     RRDD_LOG (LOG_INFO, "handle_request: unknown command: %s.", buffer);
319     return (-1);
320   }
321 } /* }}} int handle_request */
322
323 static void *connection_thread_main (void *args) /* {{{ */
324 {
325   pthread_t self;
326   int i;
327   int fd;
328   
329   fd = *((int *) args);
330
331   RRDD_LOG (LOG_DEBUG, "connection_thread_main: Adding myself to "
332       "connetion_threads[]..");
333   pthread_mutex_lock (&connetion_threads_lock);
334   {
335     pthread_t *temp;
336
337     temp = (pthread_t *) realloc (connetion_threads,
338         sizeof (pthread_t) * (connetion_threads_num + 1));
339     if (temp == NULL)
340     {
341       RRDD_LOG (LOG_ERR, "connection_thread_main: realloc failed.");
342     }
343     else
344     {
345       connetion_threads = temp;
346       connetion_threads[connetion_threads_num] = pthread_self ();
347       connetion_threads_num++;
348     }
349   }
350   pthread_mutex_unlock (&connetion_threads_lock);
351   RRDD_LOG (LOG_DEBUG, "connection_thread_main: done");
352
353   while (do_shutdown == 0)
354   {
355     struct pollfd pollfd;
356     int status;
357
358     pollfd.fd = fd;
359     pollfd.events = POLLIN | POLLPRI;
360     pollfd.revents = 0;
361
362     status = poll (&pollfd, 1, /* timeout = */ 500);
363     if (status == 0) /* timeout */
364       continue;
365     else if (status < 0) /* error */
366     {
367       status = errno;
368       if (status == EINTR)
369         continue;
370       RRDD_LOG (LOG_ERR, "connection_thread_main: poll(2) failed.");
371       continue;
372     }
373
374     if ((pollfd.revents & POLLHUP) != 0) /* normal shutdown */
375     {
376       RRDD_LOG (LOG_DEBUG, "connection_thread_main: "
377           "poll(2) returned POLLHUP.");
378       close (fd);
379       break;
380     }
381     else if ((pollfd.revents & (POLLIN | POLLPRI)) == 0)
382     {
383       RRDD_LOG (LOG_WARNING, "connection_thread_main: "
384           "poll(2) returned something unexpected: %#04hx",
385           pollfd.revents);
386       close (fd);
387       break;
388     }
389
390     status = handle_request (fd);
391     if (status != 0)
392     {
393       close (fd);
394       break;
395     }
396   }
397
398   self = pthread_self ();
399   /* Remove this thread from the connection threads list */
400   pthread_mutex_lock (&connetion_threads_lock);
401   /* Find out own index in the array */
402   for (i = 0; i < connetion_threads_num; i++)
403     if (pthread_equal (connetion_threads[i], self) != 0)
404       break;
405   assert (i < connetion_threads_num);
406
407   /* Move the trailing threads forward. */
408   if (i < (connetion_threads_num - 1))
409   {
410     memmove (connetion_threads + i,
411         connetion_threads + i + 1,
412         sizeof (pthread_t) * (connetion_threads_num - i - 1));
413   }
414
415   connetion_threads_num--;
416   pthread_mutex_unlock (&connetion_threads_lock);
417
418   free (args);
419   return (NULL);
420 } /* }}} void *connection_thread_main */
421
422 static int open_listen_socket_unix (const char *path) /* {{{ */
423 {
424   int fd;
425   struct sockaddr_un sa;
426   listen_socket_t *temp;
427   int status;
428
429   temp = (listen_socket_t *) realloc (listen_fds,
430       sizeof (listen_fds[0]) * (listen_fds_num + 1));
431   if (temp == NULL)
432   {
433     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: realloc failed.");
434     return (-1);
435   }
436   listen_fds = temp;
437   memset (listen_fds + listen_fds_num, 0, sizeof (listen_fds[0]));
438
439   fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
440   if (fd < 0)
441   {
442     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: socket(2) failed.");
443     return (-1);
444   }
445
446   memset (&sa, 0, sizeof (sa));
447   sa.sun_family = AF_UNIX;
448   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
449
450   status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
451   if (status != 0)
452   {
453     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: bind(2) failed.");
454     close (fd);
455     unlink (path);
456     return (-1);
457   }
458
459   status = listen (fd, /* backlog = */ 10);
460   if (status != 0)
461   {
462     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: listen(2) failed.");
463     close (fd);
464     unlink (path);
465     return (-1);
466   }
467   
468   listen_fds[listen_fds_num].fd = fd;
469   snprintf (listen_fds[listen_fds_num].path,
470       sizeof (listen_fds[listen_fds_num].path) - 1,
471       "unix:%s", path);
472   listen_fds_num++;
473
474   return (0);
475 } /* }}} int open_listen_socket_unix */
476
477 static int open_listen_socket (const char *addr) /* {{{ */
478 {
479   struct addrinfo ai_hints;
480   struct addrinfo *ai_res;
481   struct addrinfo *ai_ptr;
482   int status;
483
484   assert (addr != NULL);
485
486   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
487     return (open_listen_socket_unix (addr + strlen ("unix:")));
488   else if (addr[0] == '/')
489     return (open_listen_socket_unix (addr));
490
491   memset (&ai_hints, 0, sizeof (ai_hints));
492   ai_hints.ai_flags = 0;
493 #ifdef AI_ADDRCONFIG
494   ai_hints.ai_flags |= AI_ADDRCONFIG;
495 #endif
496   ai_hints.ai_family = AF_UNSPEC;
497   ai_hints.ai_socktype = SOCK_STREAM;
498
499   ai_res = NULL;
500   status = getaddrinfo (addr, DEFAULT_PORT, &ai_hints, &ai_res);
501   if (status != 0)
502   {
503     RRDD_LOG (LOG_ERR, "open_listen_socket: getaddrinfo(%s) failed: "
504         "%s", addr, gai_strerror (status));
505     return (-1);
506   }
507
508   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
509   {
510     int fd;
511     listen_socket_t *temp;
512
513     temp = (listen_socket_t *) realloc (listen_fds,
514         sizeof (listen_fds[0]) * (listen_fds_num + 1));
515     if (temp == NULL)
516     {
517       RRDD_LOG (LOG_ERR, "open_listen_socket: realloc failed.");
518       continue;
519     }
520     listen_fds = temp;
521     memset (listen_fds + listen_fds_num, 0, sizeof (listen_fds[0]));
522
523     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
524     if (fd < 0)
525     {
526       RRDD_LOG (LOG_ERR, "open_listen_socket: socket(2) failed.");
527       continue;
528     }
529
530     status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
531     if (status != 0)
532     {
533       RRDD_LOG (LOG_ERR, "open_listen_socket: bind(2) failed.");
534       close (fd);
535       continue;
536     }
537
538     status = listen (fd, /* backlog = */ 10);
539     if (status != 0)
540     {
541       RRDD_LOG (LOG_ERR, "open_listen_socket: listen(2) failed.");
542       close (fd);
543       return (-1);
544     }
545
546     listen_fds[listen_fds_num].fd = fd;
547     strncpy (listen_fds[listen_fds_num].path, addr,
548         sizeof (listen_fds[listen_fds_num].path) - 1);
549     listen_fds_num++;
550   } /* for (ai_ptr) */
551
552   return (0);
553 } /* }}} int open_listen_socket */
554
555 static int close_listen_sockets (void) /* {{{ */
556 {
557   size_t i;
558
559   for (i = 0; i < listen_fds_num; i++)
560   {
561     close (listen_fds[i].fd);
562     if (strncmp ("unix:", listen_fds[i].path, strlen ("unix:")) == 0)
563       unlink (listen_fds[i].path + strlen ("unix:"));
564   }
565
566   free (listen_fds);
567   listen_fds = NULL;
568   listen_fds_num = 0;
569
570   return (0);
571 } /* }}} int close_listen_sockets */
572
573 static void *listen_thread_main (void *args) /* {{{ */
574 {
575   struct pollfd *pollfds;
576   int pollfds_num;
577   int status;
578   int i;
579
580   for (i = 0; i < config_listen_address_list_len; i++)
581   {
582     RRDD_LOG (LOG_DEBUG, "listen_thread_main: config_listen_address_list[%i] "
583         "= %s", i, config_listen_address_list[i]);
584     open_listen_socket (config_listen_address_list[i]);
585   }
586
587   if (config_listen_address_list_len < 1)
588     open_listen_socket (RRDD_SOCK_PATH);
589
590   if (listen_fds_num < 1)
591   {
592     RRDD_LOG (LOG_ERR, "listen_thread_main: No listen sockets "
593         "could be opened. Sorry.");
594     return (NULL);
595   }
596
597   pollfds_num = listen_fds_num;
598   pollfds = (struct pollfd *) malloc (sizeof (*pollfds) * pollfds_num);
599   if (pollfds == NULL)
600   {
601     RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
602     return (NULL);
603   }
604   memset (pollfds, 0, sizeof (*pollfds) * pollfds_num);
605
606   while (do_shutdown == 0)
607   {
608     assert (pollfds_num == listen_fds_num);
609     for (i = 0; i < pollfds_num; i++)
610     {
611       pollfds[i].fd = listen_fds[i].fd;
612       pollfds[i].events = POLLIN | POLLPRI;
613       pollfds[i].revents = 0;
614     }
615
616     status = poll (pollfds, pollfds_num, /* timeout = */ -1);
617     if (status < 1)
618     {
619       status = errno;
620       if (status != EINTR)
621       {
622         RRDD_LOG (LOG_ERR, "listen_thread_main: poll(2) failed.");
623       }
624       continue;
625     }
626
627     for (i = 0; i < pollfds_num; i++)
628     {
629       int *client_sd;
630       struct sockaddr_storage client_sa;
631       socklen_t client_sa_size;
632       pthread_t tid;
633
634       if (pollfds[i].revents == 0)
635         continue;
636
637       if ((pollfds[i].revents & (POLLIN | POLLPRI)) == 0)
638       {
639         RRDD_LOG (LOG_ERR, "listen_thread_main: "
640             "poll(2) returned something unexpected for listen FD #%i.",
641             pollfds[i].fd);
642         continue;
643       }
644
645       client_sd = (int *) malloc (sizeof (int));
646       if (client_sd == NULL)
647       {
648         RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
649         continue;
650       }
651
652       client_sa_size = sizeof (client_sa);
653       *client_sd = accept (pollfds[i].fd,
654           (struct sockaddr *) &client_sa, &client_sa_size);
655       if (*client_sd < 0)
656       {
657         RRDD_LOG (LOG_ERR, "listen_thread_main: accept(2) failed.");
658         continue;
659       }
660
661       RRDD_LOG (LOG_DEBUG, "listen_thread_main: accept(2) returned fd #%i.",
662           *client_sd);
663
664       status = pthread_create (&tid, /* attr = */ NULL, connection_thread_main,
665           /* args = */ (void *) client_sd);
666       if (status != 0)
667       {
668         RRDD_LOG (LOG_ERR, "listen_thread_main: pthread_create failed.");
669         close (*client_sd);
670         free (client_sd);
671         continue;
672       }
673
674       RRDD_LOG (LOG_DEBUG, "listen_thread_main: pthread_create succeeded: "
675           "tid = %lu",
676           *((unsigned long *) &tid));
677     } /* for (pollfds_num) */
678   } /* while (do_shutdown == 0) */
679
680   close_listen_sockets ();
681
682   pthread_mutex_lock (&connetion_threads_lock);
683   while (connetion_threads_num > 0)
684   {
685     pthread_t wait_for;
686
687     wait_for = connetion_threads[0];
688
689     pthread_mutex_unlock (&connetion_threads_lock);
690     pthread_join (wait_for, /* retval = */ NULL);
691     pthread_mutex_lock (&connetion_threads_lock);
692   }
693   pthread_mutex_unlock (&connetion_threads_lock);
694
695   RRDD_LOG (LOG_DEBUG, "listen_thread_main: Exiting.");
696
697   return (NULL);
698 } /* }}} void *listen_thread_main */
699
700 static int daemonize (void) /* {{{ */
701 {
702 #if !RRDD_DEBUG
703   pid_t child;
704 #endif
705   int status;
706
707 #if !RRDD_DEBUG
708   child = fork ();
709   if (child < 0)
710   {
711     fprintf (stderr, "daemonize: fork(2) failed.\n");
712     return (-1);
713   }
714   else if (child > 0)
715   {
716     return (1);
717   }
718
719   /* Change into the /tmp directory. */
720   chdir ("/tmp");
721
722   /* Become session leader */
723   setsid ();
724
725   /* Open the first three file descriptors to /dev/null */
726   close (2);
727   close (1);
728   close (0);
729
730   open ("/dev/null", O_RDWR);
731   dup (0);
732   dup (0);
733 #endif /* RRDD_DEBUG */
734
735   {
736     struct sigaction sa;
737
738     memset (&sa, 0, sizeof (sa));
739     sa.sa_handler = sig_int_handler;
740     sigaction (SIGINT, &sa, NULL);
741
742     memset (&sa, 0, sizeof (sa));
743     sa.sa_handler = sig_term_handler;
744     sigaction (SIGINT, &sa, NULL);
745
746     memset (&sa, 0, sizeof (sa));
747     sa.sa_handler = SIG_IGN;
748     sigaction (SIGPIPE, &sa, NULL);
749   }
750
751   openlog ("rrdd", LOG_PID, LOG_DAEMON);
752
753   cache_tree = g_tree_new ((GCompareFunc) strcmp);
754   if (cache_tree == NULL)
755   {
756     RRDD_LOG (LOG_ERR, "daemonize: g_tree_new failed.");
757     return (-1);
758   }
759
760   memset (&queue_thread, 0, sizeof (queue_thread));
761   status = pthread_create (&queue_thread, /* attr = */ NULL,
762       queue_thread_main, /* args = */ NULL);
763   if (status != 0)
764   {
765     RRDD_LOG (LOG_ERR, "daemonize: pthread_create failed.");
766     return (-1);
767   }
768
769   return (0);
770 } /* }}} int daemonize */
771
772 static int cleanup (void) /* {{{ */
773 {
774   RRDD_LOG (LOG_DEBUG, "cleanup ()");
775
776   do_shutdown++;
777
778   RRDD_LOG (LOG_DEBUG, "cleanup: Joining queue_thread..");
779   pthread_cond_signal (&cache_cond);
780   pthread_join (queue_thread, /* return = */ NULL);
781   RRDD_LOG (LOG_DEBUG, "cleanup: done");
782
783   closelog ();
784
785   return (0);
786 } /* }}} int cleanup */
787
788 static int read_options (int argc, char **argv) /* {{{ */
789 {
790   int option;
791   int status = 0;
792
793   while ((option = getopt(argc, argv, "l:f:w:h?")) != -1)
794   {
795     switch (option)
796     {
797       case 'l':
798       {
799         char **temp;
800
801         temp = (char **) realloc (config_listen_address_list,
802             sizeof (char *) * (config_listen_address_list_len + 1));
803         if (temp == NULL)
804         {
805           fprintf (stderr, "read_options: realloc failed.\n");
806           return (2);
807         }
808         config_listen_address_list = temp;
809
810         temp[config_listen_address_list_len] = strdup (optarg);
811         if (temp[config_listen_address_list_len] == NULL)
812         {
813           fprintf (stderr, "read_options: strdup failed.\n");
814           return (2);
815         }
816         config_listen_address_list_len++;
817       }
818       break;
819
820       case 'f':
821       {
822         int temp;
823
824         temp = atoi (optarg);
825         if (temp > 0)
826           config_flush_interval = temp;
827         else
828         {
829           fprintf (stderr, "Invalid flush interval: %s\n", optarg);
830           status = 3;
831         }
832       }
833       break;
834
835       case 'w':
836       {
837         int temp;
838
839         temp = atoi (optarg);
840         if (temp > 0)
841           config_write_interval = temp;
842         else
843         {
844           fprintf (stderr, "Invalid write interval: %s\n", optarg);
845           status = 2;
846         }
847       }
848       break;
849
850       case 'h':
851       case '?':
852         printf ("RRDd %s  Copyright (C) 2008 Florian octo Forster\n"
853             "\n"
854             "Usage: rrdd [options]\n"
855             "\n"
856             "Valid options are:\n"
857             "  -l <address>  Socket address to listen to.\n"
858             "  -w <seconds>  Interval in which to write data.\n"
859             "  -f <seconds>  Interval in which to flush dead data.\n"
860             "\n"
861             "For more information and a detailed description of all options "
862             "please refer\n"
863             "to the rrdd(1) manual page.\n",
864             PACKAGE_VERSION);
865         status = -1;
866         break;
867     } /* switch (option) */
868   } /* while (getopt) */
869
870   return (status);
871 } /* }}} int read_options */
872
873 int main (int argc, char **argv)
874 {
875   int status;
876
877   status = read_options (argc, argv);
878   if (status != 0)
879   {
880     if (status < 0)
881       status = 0;
882     return (status);
883   }
884
885   status = daemonize ();
886   if (status == 1)
887   {
888     struct sigaction sigchld;
889
890     memset (&sigchld, 0, sizeof (sigchld));
891     sigchld.sa_handler = SIG_IGN;
892     sigaction (SIGCHLD, &sigchld, NULL);
893
894     return (0);
895   }
896   else if (status != 0)
897   {
898     fprintf (stderr, "daemonize failed, exiting.\n");
899     return (1);
900   }
901
902   listen_thread_main (NULL);
903
904   cleanup ();
905
906   return (0);
907 } /* int main */
908
909 /*
910  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
911  */