src/rrd_daemon.c: Implemented the `HELP' command.
[rrdtool.git] / src / rrd_daemon.c
1 /**
2  * RRDTool - src/rrd_daemon.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 /*
23  * First tell the compiler to stick to the C99 and POSIX standards as close as
24  * possible.
25  */
26 #ifndef __STRICT_ANSI__ /* {{{ */
27 # define __STRICT_ANSI__
28 #endif
29
30 #ifndef _ISOC99_SOURCE
31 # define _ISOC99_SOURCE
32 #endif
33
34 #ifdef _POSIX_C_SOURCE
35 # undef _POSIX_C_SOURCE
36 #endif
37 #define _POSIX_C_SOURCE 200112L
38
39 /* Single UNIX needed for strdup. */
40 #ifdef _XOPEN_SOURCE
41 # undef _XOPEN_SOURCE
42 #endif
43 #define _XOPEN_SOURCE 500
44
45 #ifndef _REENTRANT
46 # define _REENTRANT
47 #endif
48
49 #ifndef _THREAD_SAFE
50 # define _THREAD_SAFE
51 #endif
52
53 #ifdef _GNU_SOURCE
54 # undef _GNU_SOURCE
55 #endif
56 /* }}} */
57
58 /*
59  * Now for some includes..
60  */
61 #include "rrd.h" /* {{{ */
62 #include "rrd_client.h"
63
64 #include <stdlib.h>
65 #include <stdint.h>
66 #include <stdio.h>
67 #include <unistd.h>
68 #include <string.h>
69 #include <strings.h>
70 #include <stdint.h>
71 #include <inttypes.h>
72
73 #include <sys/types.h>
74 #include <sys/stat.h>
75 #include <fcntl.h>
76 #include <signal.h>
77 #include <sys/socket.h>
78 #include <sys/un.h>
79 #include <netdb.h>
80 #include <poll.h>
81 #include <syslog.h>
82 #include <pthread.h>
83 #include <errno.h>
84 #include <assert.h>
85 #include <sys/time.h>
86 #include <time.h>
87
88 #include <glib-2.0/glib.h>
89 /* }}} */
90
91 #define RRDD_LOG(severity, ...) syslog ((severity), __VA_ARGS__)
92
93 #ifndef __GNUC__
94 # define __attribute__(x) /**/
95 #endif
96
97 /*
98  * Types
99  */
100 struct listen_socket_s
101 {
102   int fd;
103   char path[PATH_MAX + 1];
104 };
105 typedef struct listen_socket_s listen_socket_t;
106
107 struct cache_item_s;
108 typedef struct cache_item_s cache_item_t;
109 struct cache_item_s
110 {
111   char *file;
112   char **values;
113   int values_num;
114   time_t last_flush_time;
115 #define CI_FLAGS_IN_TREE  0x01
116 #define CI_FLAGS_IN_QUEUE 0x02
117   int flags;
118
119   cache_item_t *next;
120 };
121
122 struct callback_flush_data_s
123 {
124   time_t now;
125   char **keys;
126   size_t keys_num;
127 };
128 typedef struct callback_flush_data_s callback_flush_data_t;
129
130 enum queue_side_e
131 {
132   HEAD,
133   TAIL
134 };
135 typedef enum queue_side_e queue_side_t;
136
137 /*
138  * Variables
139  */
140 static listen_socket_t *listen_fds = NULL;
141 static size_t listen_fds_num = 0;
142
143 static int do_shutdown = 0;
144
145 static pthread_t queue_thread;
146
147 static pthread_t *connetion_threads = NULL;
148 static pthread_mutex_t connetion_threads_lock = PTHREAD_MUTEX_INITIALIZER;
149 static int connetion_threads_num = 0;
150
151 /* Cache stuff */
152 static GTree          *cache_tree = NULL;
153 static cache_item_t   *cache_queue_head = NULL;
154 static cache_item_t   *cache_queue_tail = NULL;
155 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
156 static pthread_cond_t  cache_cond = PTHREAD_COND_INITIALIZER;
157
158 static pthread_cond_t  flush_cond = PTHREAD_COND_INITIALIZER;
159
160 static int config_write_interval = 300;
161 static int config_flush_interval = 3600;
162 static char *config_pid_file = NULL;
163 static char *config_base_dir = NULL;
164
165 static char **config_listen_address_list = NULL;
166 static int config_listen_address_list_len = 0;
167
168 static uint64_t stats_queue_length = 0;
169 static uint64_t stats_updates_total = 0;
170 static uint64_t stats_values_total = 0;
171 static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
172
173 /* 
174  * Functions
175  */
176 static void sig_int_handler (int s __attribute__((unused))) /* {{{ */
177 {
178   do_shutdown++;
179 } /* }}} void sig_int_handler */
180
181 static void sig_term_handler (int s __attribute__((unused))) /* {{{ */
182 {
183   do_shutdown++;
184 } /* }}} void sig_term_handler */
185
186 static int write_pidfile (void) /* {{{ */
187 {
188   pid_t pid;
189   char *file;
190   FILE *fh;
191
192   pid = getpid ();
193   
194   file = (config_pid_file != NULL)
195     ? config_pid_file
196     : LOCALSTATEDIR "/run/rrdcached.pid";
197
198   fh = fopen (file, "w");
199   if (fh == NULL)
200   {
201     RRDD_LOG (LOG_ERR, "write_pidfile: Opening `%s' failed.", file);
202     return (-1);
203   }
204
205   fprintf (fh, "%i\n", (int) pid);
206   fclose (fh);
207
208   return (0);
209 } /* }}} int write_pidfile */
210
211 static int remove_pidfile (void) /* {{{ */
212 {
213   char *file;
214   int status;
215
216   file = (config_pid_file != NULL)
217     ? config_pid_file
218     : LOCALSTATEDIR "/run/rrdcached.pid";
219
220   status = unlink (file);
221   if (status == 0)
222     return (0);
223   return (errno);
224 } /* }}} int remove_pidfile */
225
226 /*
227  * enqueue_cache_item:
228  * `cache_lock' must be acquired before calling this function!
229  */
230 static int enqueue_cache_item (cache_item_t *ci, /* {{{ */
231     queue_side_t side)
232 {
233   int did_insert = 0;
234
235   RRDD_LOG (LOG_DEBUG, "enqueue_cache_item: Adding %s to the update queue.",
236       ci->file);
237
238   if (ci == NULL)
239     return (-1);
240
241   if (ci->values_num == 0)
242     return (0);
243
244   if (side == HEAD)
245   {
246     if ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
247     {
248       assert (ci->next == NULL);
249       ci->next = cache_queue_head;
250       cache_queue_head = ci;
251
252       if (cache_queue_tail == NULL)
253         cache_queue_tail = cache_queue_head;
254
255       did_insert = 1;
256     }
257     else if (cache_queue_head == ci)
258     {
259       /* do nothing */
260     }
261     else /* enqueued, but not first entry */
262     {
263       cache_item_t *prev;
264
265       /* find previous entry */
266       for (prev = cache_queue_head; prev != NULL; prev = prev->next)
267         if (prev->next == ci)
268           break;
269       assert (prev != NULL);
270
271       /* move to the front */
272       prev->next = ci->next;
273       ci->next = cache_queue_head;
274       cache_queue_head = ci;
275
276       /* check if we need to adapt the tail */
277       if (cache_queue_tail == ci)
278         cache_queue_tail = prev;
279     }
280   }
281   else /* (side == TAIL) */
282   {
283     /* We don't move values back in the list.. */
284     if ((ci->flags & CI_FLAGS_IN_QUEUE) != 0)
285       return (0);
286
287     assert (ci->next == NULL);
288
289     if (cache_queue_tail == NULL)
290       cache_queue_head = ci;
291     else
292       cache_queue_tail->next = ci;
293     cache_queue_tail = ci;
294
295     did_insert = 1;
296   }
297
298   ci->flags |= CI_FLAGS_IN_QUEUE;
299
300   if (did_insert)
301   {
302     pthread_mutex_lock (&stats_lock);
303     stats_queue_length++;
304     pthread_mutex_unlock (&stats_lock);
305   }
306
307   return (0);
308 } /* }}} int enqueue_cache_item */
309
310 /*
311  * tree_callback_flush:
312  * Called via `g_tree_foreach' in `queue_thread_main'. `cache_lock' is held
313  * while this is in progress.
314  */
315 static gboolean tree_callback_flush (gpointer key, gpointer value, /* {{{ */
316     gpointer data)
317 {
318   cache_item_t *ci;
319   callback_flush_data_t *cfd;
320
321   ci = (cache_item_t *) value;
322   cfd = (callback_flush_data_t *) data;
323
324   if (((cfd->now - ci->last_flush_time) >= config_write_interval)
325       && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
326       && (ci->values_num > 0))
327   {
328     enqueue_cache_item (ci, TAIL);
329   }
330   else if (((cfd->now - ci->last_flush_time) >= config_flush_interval)
331       && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
332       && (ci->values_num <= 0))
333   {
334     char **temp;
335
336     temp = (char **) realloc (cfd->keys,
337         sizeof (char *) * (cfd->keys_num + 1));
338     if (temp == NULL)
339     {
340       RRDD_LOG (LOG_ERR, "tree_callback_flush: realloc failed.");
341       return (FALSE);
342     }
343     cfd->keys = temp;
344     /* Make really sure this points to the _same_ place */
345     assert ((char *) key == ci->file);
346     cfd->keys[cfd->keys_num] = (char *) key;
347     cfd->keys_num++;
348   }
349
350   return (FALSE);
351 } /* }}} gboolean tree_callback_flush */
352
353 static void *queue_thread_main (void *args __attribute__((unused))) /* {{{ */
354 {
355   struct timeval now;
356   struct timespec next_flush;
357
358   gettimeofday (&now, NULL);
359   next_flush.tv_sec = now.tv_sec + config_flush_interval;
360   next_flush.tv_nsec = 1000 * now.tv_usec;
361
362   pthread_mutex_lock (&cache_lock);
363   while ((do_shutdown == 0) || (cache_queue_head != NULL))
364   {
365     cache_item_t *ci;
366     char *file;
367     char **values;
368     int values_num;
369     int status;
370     int i;
371
372     /* First, check if it's time to do the cache flush. */
373     gettimeofday (&now, NULL);
374     if ((now.tv_sec > next_flush.tv_sec)
375         || ((now.tv_sec == next_flush.tv_sec)
376           && ((1000 * now.tv_usec) > next_flush.tv_nsec)))
377     {
378       callback_flush_data_t cfd;
379       size_t k;
380
381       memset (&cfd, 0, sizeof (cfd));
382       /* Pass the current time as user data so that we don't need to call
383        * `time' for each node. */
384       cfd.now = time (NULL);
385       cfd.keys = NULL;
386       cfd.keys_num = 0;
387
388       /* `tree_callback_flush' will return the keys of all values that haven't
389        * been touched in the last `config_flush_interval' seconds in `cfd'.
390        * The char*'s in this array point to the same memory as ci->file, so we
391        * don't need to free them separately. */
392       g_tree_foreach (cache_tree, tree_callback_flush, (gpointer) &cfd);
393
394       for (k = 0; k < cfd.keys_num; k++)
395       {
396         /* This must not fail. */
397         ci = (cache_item_t *) g_tree_lookup (cache_tree, cfd.keys[k]);
398         assert (ci != NULL);
399
400         /* If we end up here with values available, something's seriously
401          * messed up. */
402         assert (ci->values_num == 0);
403
404         /* Remove the node from the tree */
405         g_tree_remove (cache_tree, cfd.keys[k]);
406         cfd.keys[k] = NULL;
407
408         /* Now free and clean up `ci'. */
409         free (ci->file);
410         ci->file = NULL;
411         free (ci);
412         ci = NULL;
413       } /* for (k = 0; k < cfd.keys_num; k++) */
414
415       if (cfd.keys != NULL)
416       {
417         free (cfd.keys);
418         cfd.keys = NULL;
419       }
420
421       /* Determine the time of the next cache flush. */
422       while (next_flush.tv_sec < now.tv_sec)
423         next_flush.tv_sec += config_flush_interval;
424     }
425
426     /* Now, check if there's something to store away. If not, wait until
427      * something comes in or it's time to do the cache flush. */
428     if (cache_queue_head == NULL)
429     {
430       status = pthread_cond_timedwait (&cache_cond, &cache_lock, &next_flush);
431       if ((status != 0) && (status != ETIMEDOUT))
432       {
433         RRDD_LOG (LOG_ERR, "queue_thread_main: "
434             "pthread_cond_timedwait returned %i.", status);
435       }
436     }
437
438     /* Check if a value has arrived. This may be NULL if we timed out or there
439      * was an interrupt such as a signal. */
440     if (cache_queue_head == NULL)
441       continue;
442
443     ci = cache_queue_head;
444
445     /* copy the relevant parts */
446     file = strdup (ci->file);
447     if (file == NULL)
448     {
449       RRDD_LOG (LOG_ERR, "queue_thread_main: strdup failed.");
450       continue;
451     }
452
453     values = ci->values;
454     values_num = ci->values_num;
455
456     ci->values = NULL;
457     ci->values_num = 0;
458
459     ci->last_flush_time = time (NULL);
460     ci->flags &= ~(CI_FLAGS_IN_QUEUE);
461
462     cache_queue_head = ci->next;
463     if (cache_queue_head == NULL)
464       cache_queue_tail = NULL;
465     ci->next = NULL;
466
467     pthread_mutex_lock (&stats_lock);
468     assert (stats_queue_length > 0);
469     stats_queue_length--;
470     pthread_mutex_unlock (&stats_lock);
471
472     pthread_mutex_unlock (&cache_lock);
473
474     RRDD_LOG (LOG_DEBUG, "queue_thread_main: rrd_update (%s, %i, %p)",
475         file, values_num, (void *) values);
476
477     status = rrd_update_r (file, NULL, values_num, (void *) values);
478     if (status != 0)
479     {
480       RRDD_LOG (LOG_ERR, "queue_thread_main: "
481           "rrd_update_r failed with status %i.",
482           status);
483     }
484
485     free (file);
486     for (i = 0; i < values_num; i++)
487       free (values[i]);
488
489     pthread_mutex_lock (&stats_lock);
490     stats_updates_total++;
491     stats_values_total += values_num;
492     pthread_mutex_unlock (&stats_lock);
493
494     pthread_mutex_lock (&cache_lock);
495     pthread_cond_broadcast (&flush_cond);
496   } /* while (do_shutdown == 0) */
497   pthread_mutex_unlock (&cache_lock);
498
499   RRDD_LOG (LOG_DEBUG, "queue_thread_main: Exiting.");
500
501   return (NULL);
502 } /* }}} void *queue_thread_main */
503
504 static int buffer_get_field (char **buffer_ret, /* {{{ */
505     size_t *buffer_size_ret, char **field_ret)
506 {
507   char *buffer;
508   size_t buffer_pos;
509   size_t buffer_size;
510   char *field;
511   size_t field_size;
512   int status;
513
514   buffer = *buffer_ret;
515   buffer_pos = 0;
516   buffer_size = *buffer_size_ret;
517   field = *buffer_ret;
518   field_size = 0;
519
520   if (buffer_size <= 0)
521     return (-1);
522
523   /* This is ensured by `handle_request'. */
524   assert (buffer[buffer_size - 1] == ' ');
525
526   status = -1;
527   while (buffer_pos < buffer_size)
528   {
529     /* Check for end-of-field or end-of-buffer */
530     if (buffer[buffer_pos] == ' ')
531     {
532       field[field_size] = 0;
533       field_size++;
534       buffer_pos++;
535       status = 0;
536       break;
537     }
538     /* Handle escaped characters. */
539     else if (buffer[buffer_pos] == '\\')
540     {
541       if (buffer_pos >= (buffer_size - 1))
542         break;
543       buffer_pos++;
544       field[field_size] = buffer[buffer_pos];
545       field_size++;
546       buffer_pos++;
547     }
548     /* Normal operation */ 
549     else
550     {
551       field[field_size] = buffer[buffer_pos];
552       field_size++;
553       buffer_pos++;
554     }
555   } /* while (buffer_pos < buffer_size) */
556
557   if (status != 0)
558     return (status);
559
560   *buffer_ret = buffer + buffer_pos;
561   *buffer_size_ret = buffer_size - buffer_pos;
562   *field_ret = field;
563
564   return (0);
565 } /* }}} int buffer_get_field */
566
567 static int flush_file (const char *filename) /* {{{ */
568 {
569   cache_item_t *ci;
570
571   pthread_mutex_lock (&cache_lock);
572
573   ci = (cache_item_t *) g_tree_lookup (cache_tree, filename);
574   if (ci == NULL)
575   {
576     pthread_mutex_unlock (&cache_lock);
577     return (ENOENT);
578   }
579
580   /* Enqueue at head */
581   enqueue_cache_item (ci, HEAD);
582   pthread_cond_signal (&cache_cond);
583
584   while ((ci->flags & CI_FLAGS_IN_QUEUE) != 0)
585   {
586     ci = NULL;
587
588     pthread_cond_wait (&flush_cond, &cache_lock);
589
590     ci = g_tree_lookup (cache_tree, filename);
591     if (ci == NULL)
592     {
593       RRDD_LOG (LOG_ERR, "flush_file: Tree node went away "
594           "while waiting for flush.");
595       pthread_mutex_unlock (&cache_lock);
596       return (-1);
597     }
598   }
599
600   pthread_mutex_unlock (&cache_lock);
601   return (0);
602 } /* }}} int flush_file */
603
604 static int handle_request_help (int fd, /* {{{ */
605     char *buffer, size_t buffer_size)
606 {
607   int status;
608   char **help_text;
609   size_t help_text_len;
610   char *command;
611   size_t i;
612
613   char *help_help[] =
614   {
615     "4 Command overview\n",
616     "FLUSH <filename>\n",
617     "HELP [<command>]\n",
618     "UPDATE <filename> <values> [<values> ...]\n",
619     "STATS\n"
620   };
621   size_t help_help_len = sizeof (help_help) / sizeof (help_help[0]);
622
623   char *help_flush[] =
624   {
625     "4 Help for FLUSH\n",
626     "Usage: FLUSH <filename>\n",
627     "\n",
628     "Adds the given filename to the head of the update queue and returns\n",
629     "after is has been dequeued.\n"
630   };
631   size_t help_flush_len = sizeof (help_flush) / sizeof (help_flush[0]);
632
633   char *help_update[] =
634   {
635     "9 Help for UPDATE\n",
636     "Usage: UPDATE <filename> <values> [<values> ...]\n"
637     "\n",
638     "Adds the given file to the internal cache if it is not yet known and\n",
639     "appends the given value(s) to the entry. See the rrdcached(1) manpage\n",
640     "for details.\n",
641     "\n",
642     "Each <values> has the following form:\n",
643     "  <values> = <time>:<value>[:<value>[...]]\n",
644     "See the rrdupdate(1) manpage for details.\n"
645   };
646   size_t help_update_len = sizeof (help_update) / sizeof (help_update[0]);
647
648   char *help_stats[] =
649   {
650     "4 Help for STATS\n",
651     "Usage: STATS\n",
652     "\n",
653     "Returns some performance counters, see the rrdcached(1) manpage for\n",
654     "a description of the values.\n"
655   };
656   size_t help_stats_len = sizeof (help_stats) / sizeof (help_stats[0]);
657
658   status = buffer_get_field (&buffer, &buffer_size, &command);
659   if (status != 0)
660   {
661     help_text = help_help;
662     help_text_len = help_help_len;
663   }
664   else
665   {
666     if (strcasecmp (command, "update") == 0)
667     {
668       help_text = help_update;
669       help_text_len = help_update_len;
670     }
671     else if (strcasecmp (command, "flush") == 0)
672     {
673       help_text = help_flush;
674       help_text_len = help_flush_len;
675     }
676     else if (strcasecmp (command, "stats") == 0)
677     {
678       help_text = help_stats;
679       help_text_len = help_stats_len;
680     }
681     else
682     {
683       help_text = help_help;
684       help_text_len = help_help_len;
685     }
686   }
687
688   for (i = 0; i < help_text_len; i++)
689   {
690     status = write (fd, help_text[i], strlen (help_text[i]));
691     if (status < 0)
692     {
693       status = errno;
694       RRDD_LOG (LOG_ERR, "handle_request_help: write(2) returned an error.");
695       return (status);
696     }
697   }
698
699   return (0);
700 } /* }}} int handle_request_help */
701
702 static int handle_request_stats (int fd, /* {{{ */
703     char *buffer __attribute__((unused)),
704     size_t buffer_size __attribute__((unused)))
705 {
706   int status;
707   char outbuf[4096];
708
709   uint64_t copy_queue_length;
710   uint64_t copy_updates_total;
711   uint64_t copy_values_total;
712
713   uint64_t tree_nodes;
714   uint64_t tree_depth;
715
716   pthread_mutex_lock (&stats_lock);
717   copy_queue_length  = stats_queue_length;
718   copy_updates_total = stats_updates_total;
719   copy_values_total  = stats_values_total;
720   pthread_mutex_unlock (&stats_lock);
721
722   pthread_mutex_lock (&cache_lock);
723   tree_nodes = (uint64_t) g_tree_nnodes (cache_tree);
724   tree_depth = (uint64_t) g_tree_height (cache_tree);
725   pthread_mutex_unlock (&cache_lock);
726
727 #define RRDD_STATS_SEND \
728   outbuf[sizeof (outbuf) - 1] = 0; \
729   status = write (fd, outbuf, strlen (outbuf)); \
730   if (status < 0) \
731   { \
732     status = errno; \
733     RRDD_LOG (LOG_INFO, "handle_request_stats: write(2) returned an error."); \
734     return (status); \
735   }
736
737   strncpy (outbuf, "5 Statistics follow\n", sizeof (outbuf));
738   RRDD_STATS_SEND;
739
740   snprintf (outbuf, sizeof (outbuf),
741       "QueueLength: %"PRIu64"\n", copy_queue_length);
742   RRDD_STATS_SEND;
743
744   snprintf (outbuf, sizeof (outbuf),
745       "UpdatesWritten: %"PRIu64"\n", copy_updates_total);
746   RRDD_STATS_SEND;
747
748   snprintf (outbuf, sizeof (outbuf),
749       "ValuesWritten: %"PRIu64"\n", copy_values_total);
750   RRDD_STATS_SEND;
751
752   snprintf (outbuf, sizeof (outbuf),
753       "TreeNodesNumber: %"PRIu64"\n", tree_nodes);
754   RRDD_STATS_SEND;
755
756   snprintf (outbuf, sizeof (outbuf),
757       "TreeDepth: %"PRIu64"\n", tree_depth);
758   RRDD_STATS_SEND;
759
760   return (0);
761 } /* }}} int handle_request_stats */
762
763 static int handle_request_flush (int fd, /* {{{ */
764     char *buffer, size_t buffer_size)
765 {
766   char *file;
767   int status;
768   char result[4096];
769
770   status = buffer_get_field (&buffer, &buffer_size, &file);
771   if (status != 0)
772   {
773     strncpy (result, "-1 Usage: flush <filename>\n", sizeof (result));
774   }
775   else
776   {
777     status = flush_file (file);
778     if (status == 0)
779       snprintf (result, sizeof (result), "0 Successfully flushed %s.\n", file);
780     else if (status == ENOENT)
781       snprintf (result, sizeof (result), "-1 No such file: %s.\n", file);
782     else if (status < 0)
783       strncpy (result, "-1 Internal error.\n", sizeof (result));
784     else
785       snprintf (result, sizeof (result), "-1 Failed with status %i.\n", status);
786   }
787   result[sizeof (result) - 1] = 0;
788
789   status = write (fd, result, strlen (result));
790   if (status < 0)
791   {
792     status = errno;
793     RRDD_LOG (LOG_INFO, "handle_request_flush: write(2) returned an error.");
794     return (status);
795   }
796
797   return (0);
798 } /* }}} int handle_request_flush */
799
800 static int handle_request_update (int fd, /* {{{ */
801     char *buffer, size_t buffer_size)
802 {
803   char *file;
804   int values_num = 0;
805   int status;
806
807   time_t now;
808
809   cache_item_t *ci;
810   char answer[4096];
811
812   now = time (NULL);
813
814   status = buffer_get_field (&buffer, &buffer_size, &file);
815   if (status != 0)
816   {
817     RRDD_LOG (LOG_INFO, "handle_request_update: Cannot get file name.");
818     return (-1);
819   }
820
821   pthread_mutex_lock (&cache_lock);
822
823   ci = g_tree_lookup (cache_tree, file);
824   if (ci == NULL) /* {{{ */
825   {
826     ci = (cache_item_t *) malloc (sizeof (cache_item_t));
827     if (ci == NULL)
828     {
829       pthread_mutex_unlock (&cache_lock);
830       RRDD_LOG (LOG_ERR, "handle_request_update: malloc failed.");
831       return (-1);
832     }
833     memset (ci, 0, sizeof (cache_item_t));
834
835     ci->file = strdup (file);
836     if (ci->file == NULL)
837     {
838       pthread_mutex_unlock (&cache_lock);
839       RRDD_LOG (LOG_ERR, "handle_request_update: malloc failed.");
840       free (ci);
841       return (-1);
842     }
843
844     ci->values = NULL;
845     ci->values_num = 0;
846     ci->last_flush_time = now;
847     ci->flags = CI_FLAGS_IN_TREE;
848
849     g_tree_insert (cache_tree, (void *) ci->file, (void *) ci);
850
851     RRDD_LOG (LOG_DEBUG, "handle_request_update: Created new tree node %s.",
852         ci->file);
853   } /* }}} */
854   assert (ci != NULL);
855
856   while (buffer_size > 0)
857   {
858     char **temp;
859     char *value;
860
861     status = buffer_get_field (&buffer, &buffer_size, &value);
862     if (status != 0)
863     {
864       RRDD_LOG (LOG_INFO, "handle_request_update: Error reading field.");
865       break;
866     }
867
868     temp = (char **) realloc (ci->values,
869         sizeof (char *) * (ci->values_num + 1));
870     if (temp == NULL)
871     {
872       RRDD_LOG (LOG_ERR, "handle_request_update: realloc failed.");
873       continue;
874     }
875     ci->values = temp;
876
877     ci->values[ci->values_num] = strdup (value);
878     if (ci->values[ci->values_num] == NULL)
879     {
880       RRDD_LOG (LOG_ERR, "handle_request_update: strdup failed.");
881       continue;
882     }
883     ci->values_num++;
884
885     values_num++;
886   }
887
888   if (((now - ci->last_flush_time) >= config_write_interval)
889       && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
890       && (ci->values_num > 0))
891   {
892     enqueue_cache_item (ci, TAIL);
893     pthread_cond_signal (&cache_cond);
894   }
895
896   pthread_mutex_unlock (&cache_lock);
897
898   snprintf (answer, sizeof (answer), "0 Enqueued %i value(s)\n", values_num);
899   answer[sizeof (answer) - 1] = 0;
900
901   status = write (fd, answer, strlen (answer));
902   if (status < 0)
903   {
904     status = errno;
905     RRDD_LOG (LOG_INFO, "handle_request_update: write(2) returned an error.");
906     return (status);
907   }
908
909   return (0);
910 } /* }}} int handle_request_update */
911
912 static int handle_request (int fd) /* {{{ */
913 {
914   char buffer[4096];
915   size_t buffer_size;
916   char *buffer_ptr;
917   char *command;
918   int status;
919
920   status = read (fd, buffer, sizeof (buffer));
921   if (status == 0)
922   {
923     return (1);
924   }
925   else if (status < 0)
926   {
927     RRDD_LOG (LOG_ERR, "handle_request: read(2) failed.");
928     return (-1);
929   }
930   buffer_size = status;
931   assert (((size_t) buffer_size) <= sizeof (buffer));
932
933   if (buffer[buffer_size - 1] != '\n')
934   {
935     RRDD_LOG (LOG_INFO, "handle_request: malformed request.");
936     return (-1);
937   }
938
939   /* Accept Windows style line endings, too */
940   if ((buffer_size > 2) && (buffer[buffer_size - 2] == '\r'))
941   {
942     buffer_size--;
943     buffer[buffer_size - 1] = '\n';
944   }
945
946   /* Place the normal field separator at the end to simplify
947    * `buffer_get_field's work. */
948   buffer[buffer_size - 1] = ' ';
949
950   buffer_ptr = buffer;
951   command = NULL;
952   status = buffer_get_field (&buffer_ptr, &buffer_size, &command);
953   if (status != 0)
954   {
955     RRDD_LOG (LOG_INFO, "handle_request: Unable parse command.");
956     return (-1);
957   }
958
959   if (strcasecmp (command, "update") == 0)
960   {
961     return (handle_request_update (fd, buffer_ptr, buffer_size));
962   }
963   else if (strcasecmp (command, "flush") == 0)
964   {
965     return (handle_request_flush (fd, buffer_ptr, buffer_size));
966   }
967   else if (strcasecmp (command, "stats") == 0)
968   {
969     return (handle_request_stats (fd, buffer_ptr, buffer_size));
970   }
971   else if (strcasecmp (command, "help") == 0)
972   {
973     return (handle_request_help (fd, buffer_ptr, buffer_size));
974   }
975   else
976   {
977     char result[4096];
978
979     snprintf (result, sizeof (result), "-1 Unknown command: %s\n", command);
980     result[sizeof (result) - 1] = 0;
981
982     status = write (fd, result, strlen (result));
983     if (status < 0)
984     {
985       RRDD_LOG (LOG_ERR, "handle_request: write(2) failed.");
986       return (-1);
987     }
988   }
989
990   return (0);
991 } /* }}} int handle_request */
992
993 static void *connection_thread_main (void *args /* {{{ */
994     __attribute__((unused)))
995 {
996   pthread_t self;
997   int i;
998   int fd;
999   
1000   fd = *((int *) args);
1001
1002   pthread_mutex_lock (&connetion_threads_lock);
1003   {
1004     pthread_t *temp;
1005
1006     temp = (pthread_t *) realloc (connetion_threads,
1007         sizeof (pthread_t) * (connetion_threads_num + 1));
1008     if (temp == NULL)
1009     {
1010       RRDD_LOG (LOG_ERR, "connection_thread_main: realloc failed.");
1011     }
1012     else
1013     {
1014       connetion_threads = temp;
1015       connetion_threads[connetion_threads_num] = pthread_self ();
1016       connetion_threads_num++;
1017     }
1018   }
1019   pthread_mutex_unlock (&connetion_threads_lock);
1020
1021   while (do_shutdown == 0)
1022   {
1023     struct pollfd pollfd;
1024     int status;
1025
1026     pollfd.fd = fd;
1027     pollfd.events = POLLIN | POLLPRI;
1028     pollfd.revents = 0;
1029
1030     status = poll (&pollfd, 1, /* timeout = */ 500);
1031     if (status == 0) /* timeout */
1032       continue;
1033     else if (status < 0) /* error */
1034     {
1035       status = errno;
1036       if (status == EINTR)
1037         continue;
1038       RRDD_LOG (LOG_ERR, "connection_thread_main: poll(2) failed.");
1039       continue;
1040     }
1041
1042     if ((pollfd.revents & POLLHUP) != 0) /* normal shutdown */
1043     {
1044       close (fd);
1045       break;
1046     }
1047     else if ((pollfd.revents & (POLLIN | POLLPRI)) == 0)
1048     {
1049       RRDD_LOG (LOG_WARNING, "connection_thread_main: "
1050           "poll(2) returned something unexpected: %#04hx",
1051           pollfd.revents);
1052       close (fd);
1053       break;
1054     }
1055
1056     status = handle_request (fd);
1057     if (status != 0)
1058     {
1059       close (fd);
1060       break;
1061     }
1062   }
1063
1064   self = pthread_self ();
1065   /* Remove this thread from the connection threads list */
1066   pthread_mutex_lock (&connetion_threads_lock);
1067   /* Find out own index in the array */
1068   for (i = 0; i < connetion_threads_num; i++)
1069     if (pthread_equal (connetion_threads[i], self) != 0)
1070       break;
1071   assert (i < connetion_threads_num);
1072
1073   /* Move the trailing threads forward. */
1074   if (i < (connetion_threads_num - 1))
1075   {
1076     memmove (connetion_threads + i,
1077         connetion_threads + i + 1,
1078         sizeof (pthread_t) * (connetion_threads_num - i - 1));
1079   }
1080
1081   connetion_threads_num--;
1082   pthread_mutex_unlock (&connetion_threads_lock);
1083
1084   free (args);
1085   return (NULL);
1086 } /* }}} void *connection_thread_main */
1087
1088 static int open_listen_socket_unix (const char *path) /* {{{ */
1089 {
1090   int fd;
1091   struct sockaddr_un sa;
1092   listen_socket_t *temp;
1093   int status;
1094
1095   temp = (listen_socket_t *) realloc (listen_fds,
1096       sizeof (listen_fds[0]) * (listen_fds_num + 1));
1097   if (temp == NULL)
1098   {
1099     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: realloc failed.");
1100     return (-1);
1101   }
1102   listen_fds = temp;
1103   memset (listen_fds + listen_fds_num, 0, sizeof (listen_fds[0]));
1104
1105   fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
1106   if (fd < 0)
1107   {
1108     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: socket(2) failed.");
1109     return (-1);
1110   }
1111
1112   memset (&sa, 0, sizeof (sa));
1113   sa.sun_family = AF_UNIX;
1114   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
1115
1116   status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
1117   if (status != 0)
1118   {
1119     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: bind(2) failed.");
1120     close (fd);
1121     unlink (path);
1122     return (-1);
1123   }
1124
1125   status = listen (fd, /* backlog = */ 10);
1126   if (status != 0)
1127   {
1128     RRDD_LOG (LOG_ERR, "open_listen_socket_unix: listen(2) failed.");
1129     close (fd);
1130     unlink (path);
1131     return (-1);
1132   }
1133   
1134   listen_fds[listen_fds_num].fd = fd;
1135   snprintf (listen_fds[listen_fds_num].path,
1136       sizeof (listen_fds[listen_fds_num].path) - 1,
1137       "unix:%s", path);
1138   listen_fds_num++;
1139
1140   return (0);
1141 } /* }}} int open_listen_socket_unix */
1142
1143 static int open_listen_socket (const char *addr) /* {{{ */
1144 {
1145   struct addrinfo ai_hints;
1146   struct addrinfo *ai_res;
1147   struct addrinfo *ai_ptr;
1148   int status;
1149
1150   assert (addr != NULL);
1151
1152   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
1153     return (open_listen_socket_unix (addr + strlen ("unix:")));
1154   else if (addr[0] == '/')
1155     return (open_listen_socket_unix (addr));
1156
1157   memset (&ai_hints, 0, sizeof (ai_hints));
1158   ai_hints.ai_flags = 0;
1159 #ifdef AI_ADDRCONFIG
1160   ai_hints.ai_flags |= AI_ADDRCONFIG;
1161 #endif
1162   ai_hints.ai_family = AF_UNSPEC;
1163   ai_hints.ai_socktype = SOCK_STREAM;
1164
1165   ai_res = NULL;
1166   status = getaddrinfo (addr, RRDCACHED_DEFAULT_PORT, &ai_hints, &ai_res);
1167   if (status != 0)
1168   {
1169     RRDD_LOG (LOG_ERR, "open_listen_socket: getaddrinfo(%s) failed: "
1170         "%s", addr, gai_strerror (status));
1171     return (-1);
1172   }
1173
1174   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
1175   {
1176     int fd;
1177     listen_socket_t *temp;
1178
1179     temp = (listen_socket_t *) realloc (listen_fds,
1180         sizeof (listen_fds[0]) * (listen_fds_num + 1));
1181     if (temp == NULL)
1182     {
1183       RRDD_LOG (LOG_ERR, "open_listen_socket: realloc failed.");
1184       continue;
1185     }
1186     listen_fds = temp;
1187     memset (listen_fds + listen_fds_num, 0, sizeof (listen_fds[0]));
1188
1189     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
1190     if (fd < 0)
1191     {
1192       RRDD_LOG (LOG_ERR, "open_listen_socket: socket(2) failed.");
1193       continue;
1194     }
1195
1196     status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
1197     if (status != 0)
1198     {
1199       RRDD_LOG (LOG_ERR, "open_listen_socket: bind(2) failed.");
1200       close (fd);
1201       continue;
1202     }
1203
1204     status = listen (fd, /* backlog = */ 10);
1205     if (status != 0)
1206     {
1207       RRDD_LOG (LOG_ERR, "open_listen_socket: listen(2) failed.");
1208       close (fd);
1209       return (-1);
1210     }
1211
1212     listen_fds[listen_fds_num].fd = fd;
1213     strncpy (listen_fds[listen_fds_num].path, addr,
1214         sizeof (listen_fds[listen_fds_num].path) - 1);
1215     listen_fds_num++;
1216   } /* for (ai_ptr) */
1217
1218   return (0);
1219 } /* }}} int open_listen_socket */
1220
1221 static int close_listen_sockets (void) /* {{{ */
1222 {
1223   size_t i;
1224
1225   for (i = 0; i < listen_fds_num; i++)
1226   {
1227     close (listen_fds[i].fd);
1228     if (strncmp ("unix:", listen_fds[i].path, strlen ("unix:")) == 0)
1229       unlink (listen_fds[i].path + strlen ("unix:"));
1230   }
1231
1232   free (listen_fds);
1233   listen_fds = NULL;
1234   listen_fds_num = 0;
1235
1236   return (0);
1237 } /* }}} int close_listen_sockets */
1238
1239 static void *listen_thread_main (void *args __attribute__((unused))) /* {{{ */
1240 {
1241   struct pollfd *pollfds;
1242   int pollfds_num;
1243   int status;
1244   int i;
1245
1246   for (i = 0; i < config_listen_address_list_len; i++)
1247   {
1248     RRDD_LOG (LOG_DEBUG, "listen_thread_main: config_listen_address_list[%i] "
1249         "= %s", i, config_listen_address_list[i]);
1250     open_listen_socket (config_listen_address_list[i]);
1251   }
1252
1253   if (config_listen_address_list_len < 1)
1254     open_listen_socket (RRDCACHED_DEFAULT_ADDRESS);
1255
1256   if (listen_fds_num < 1)
1257   {
1258     RRDD_LOG (LOG_ERR, "listen_thread_main: No listen sockets "
1259         "could be opened. Sorry.");
1260     return (NULL);
1261   }
1262
1263   pollfds_num = listen_fds_num;
1264   pollfds = (struct pollfd *) malloc (sizeof (*pollfds) * pollfds_num);
1265   if (pollfds == NULL)
1266   {
1267     RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
1268     return (NULL);
1269   }
1270   memset (pollfds, 0, sizeof (*pollfds) * pollfds_num);
1271
1272   while (do_shutdown == 0)
1273   {
1274     assert (pollfds_num == ((int) listen_fds_num));
1275     for (i = 0; i < pollfds_num; i++)
1276     {
1277       pollfds[i].fd = listen_fds[i].fd;
1278       pollfds[i].events = POLLIN | POLLPRI;
1279       pollfds[i].revents = 0;
1280     }
1281
1282     status = poll (pollfds, pollfds_num, /* timeout = */ -1);
1283     if (status < 1)
1284     {
1285       status = errno;
1286       if (status != EINTR)
1287       {
1288         RRDD_LOG (LOG_ERR, "listen_thread_main: poll(2) failed.");
1289       }
1290       continue;
1291     }
1292
1293     for (i = 0; i < pollfds_num; i++)
1294     {
1295       int *client_sd;
1296       struct sockaddr_storage client_sa;
1297       socklen_t client_sa_size;
1298       pthread_t tid;
1299
1300       if (pollfds[i].revents == 0)
1301         continue;
1302
1303       if ((pollfds[i].revents & (POLLIN | POLLPRI)) == 0)
1304       {
1305         RRDD_LOG (LOG_ERR, "listen_thread_main: "
1306             "poll(2) returned something unexpected for listen FD #%i.",
1307             pollfds[i].fd);
1308         continue;
1309       }
1310
1311       client_sd = (int *) malloc (sizeof (int));
1312       if (client_sd == NULL)
1313       {
1314         RRDD_LOG (LOG_ERR, "listen_thread_main: malloc failed.");
1315         continue;
1316       }
1317
1318       client_sa_size = sizeof (client_sa);
1319       *client_sd = accept (pollfds[i].fd,
1320           (struct sockaddr *) &client_sa, &client_sa_size);
1321       if (*client_sd < 0)
1322       {
1323         RRDD_LOG (LOG_ERR, "listen_thread_main: accept(2) failed.");
1324         continue;
1325       }
1326
1327       status = pthread_create (&tid, /* attr = */ NULL, connection_thread_main,
1328           /* args = */ (void *) client_sd);
1329       if (status != 0)
1330       {
1331         RRDD_LOG (LOG_ERR, "listen_thread_main: pthread_create failed.");
1332         close (*client_sd);
1333         free (client_sd);
1334         continue;
1335       }
1336     } /* for (pollfds_num) */
1337   } /* while (do_shutdown == 0) */
1338
1339   close_listen_sockets ();
1340
1341   pthread_mutex_lock (&connetion_threads_lock);
1342   while (connetion_threads_num > 0)
1343   {
1344     pthread_t wait_for;
1345
1346     wait_for = connetion_threads[0];
1347
1348     pthread_mutex_unlock (&connetion_threads_lock);
1349     pthread_join (wait_for, /* retval = */ NULL);
1350     pthread_mutex_lock (&connetion_threads_lock);
1351   }
1352   pthread_mutex_unlock (&connetion_threads_lock);
1353
1354   RRDD_LOG (LOG_DEBUG, "listen_thread_main: Exiting.");
1355
1356   return (NULL);
1357 } /* }}} void *listen_thread_main */
1358
1359 static int daemonize (void) /* {{{ */
1360 {
1361   pid_t child;
1362   int status;
1363   char *base_dir;
1364
1365   /* These structures are static, because `sigaction' behaves weird if the are
1366    * overwritten.. */
1367   static struct sigaction sa_int;
1368   static struct sigaction sa_term;
1369   static struct sigaction sa_pipe;
1370
1371   child = fork ();
1372   if (child < 0)
1373   {
1374     fprintf (stderr, "daemonize: fork(2) failed.\n");
1375     return (-1);
1376   }
1377   else if (child > 0)
1378   {
1379     return (1);
1380   }
1381
1382   /* Change into the /tmp directory. */
1383   base_dir = (config_base_dir != NULL)
1384     ? config_base_dir
1385     : "/tmp";
1386   status = chdir (base_dir);
1387   if (status != 0)
1388   {
1389     fprintf (stderr, "daemonize: chdir (%s) failed.\n", base_dir);
1390     return (-1);
1391   }
1392
1393   /* Become session leader */
1394   setsid ();
1395
1396   /* Open the first three file descriptors to /dev/null */
1397   close (2);
1398   close (1);
1399   close (0);
1400
1401   open ("/dev/null", O_RDWR);
1402   dup (0);
1403   dup (0);
1404
1405   /* Install signal handlers */
1406   memset (&sa_int, 0, sizeof (sa_int));
1407   sa_int.sa_handler = sig_int_handler;
1408   sigaction (SIGINT, &sa_int, NULL);
1409
1410   memset (&sa_term, 0, sizeof (sa_term));
1411   sa_term.sa_handler = sig_term_handler;
1412   sigaction (SIGINT, &sa_term, NULL);
1413
1414   memset (&sa_pipe, 0, sizeof (sa_pipe));
1415   sa_pipe.sa_handler = SIG_IGN;
1416   sigaction (SIGPIPE, &sa_pipe, NULL);
1417
1418   openlog ("rrdcached", LOG_PID, LOG_DAEMON);
1419
1420   cache_tree = g_tree_new ((GCompareFunc) strcmp);
1421   if (cache_tree == NULL)
1422   {
1423     RRDD_LOG (LOG_ERR, "daemonize: g_tree_new failed.");
1424     return (-1);
1425   }
1426
1427   memset (&queue_thread, 0, sizeof (queue_thread));
1428   status = pthread_create (&queue_thread, /* attr = */ NULL,
1429       queue_thread_main, /* args = */ NULL);
1430   if (status != 0)
1431   {
1432     RRDD_LOG (LOG_ERR, "daemonize: pthread_create failed.");
1433     return (-1);
1434   }
1435
1436   write_pidfile ();
1437
1438   return (0);
1439 } /* }}} int daemonize */
1440
1441 static int cleanup (void) /* {{{ */
1442 {
1443   RRDD_LOG (LOG_DEBUG, "cleanup ()");
1444
1445   do_shutdown++;
1446
1447   RRDD_LOG (LOG_DEBUG, "cleanup: Joining queue_thread..");
1448   pthread_cond_signal (&cache_cond);
1449   pthread_join (queue_thread, /* return = */ NULL);
1450   RRDD_LOG (LOG_DEBUG, "cleanup: done");
1451
1452   remove_pidfile ();
1453
1454   closelog ();
1455
1456   return (0);
1457 } /* }}} int cleanup */
1458
1459 static int read_options (int argc, char **argv) /* {{{ */
1460 {
1461   int option;
1462   int status = 0;
1463
1464   while ((option = getopt(argc, argv, "l:f:w:b:p:h?")) != -1)
1465   {
1466     switch (option)
1467     {
1468       case 'l':
1469       {
1470         char **temp;
1471
1472         temp = (char **) realloc (config_listen_address_list,
1473             sizeof (char *) * (config_listen_address_list_len + 1));
1474         if (temp == NULL)
1475         {
1476           fprintf (stderr, "read_options: realloc failed.\n");
1477           return (2);
1478         }
1479         config_listen_address_list = temp;
1480
1481         temp[config_listen_address_list_len] = strdup (optarg);
1482         if (temp[config_listen_address_list_len] == NULL)
1483         {
1484           fprintf (stderr, "read_options: strdup failed.\n");
1485           return (2);
1486         }
1487         config_listen_address_list_len++;
1488       }
1489       break;
1490
1491       case 'f':
1492       {
1493         int temp;
1494
1495         temp = atoi (optarg);
1496         if (temp > 0)
1497           config_flush_interval = temp;
1498         else
1499         {
1500           fprintf (stderr, "Invalid flush interval: %s\n", optarg);
1501           status = 3;
1502         }
1503       }
1504       break;
1505
1506       case 'w':
1507       {
1508         int temp;
1509
1510         temp = atoi (optarg);
1511         if (temp > 0)
1512           config_write_interval = temp;
1513         else
1514         {
1515           fprintf (stderr, "Invalid write interval: %s\n", optarg);
1516           status = 2;
1517         }
1518       }
1519       break;
1520
1521       case 'b':
1522       {
1523         size_t len;
1524
1525         if (config_base_dir != NULL)
1526           free (config_base_dir);
1527         config_base_dir = strdup (optarg);
1528         if (config_base_dir == NULL)
1529         {
1530           fprintf (stderr, "read_options: strdup failed.\n");
1531           return (3);
1532         }
1533
1534         len = strlen (config_base_dir);
1535         while ((len > 0) && (config_base_dir[len - 1] == '/'))
1536         {
1537           config_base_dir[len - 1] = 0;
1538           len--;
1539         }
1540
1541         if (len < 1)
1542         {
1543           fprintf (stderr, "Invalid base directory: %s\n", optarg);
1544           return (4);
1545         }
1546       }
1547       break;
1548
1549       case 'p':
1550       {
1551         if (config_pid_file != NULL)
1552           free (config_pid_file);
1553         config_pid_file = strdup (optarg);
1554         if (config_pid_file == NULL)
1555         {
1556           fprintf (stderr, "read_options: strdup failed.\n");
1557           return (3);
1558         }
1559       }
1560       break;
1561
1562       case 'h':
1563       case '?':
1564         printf ("RRDd %s  Copyright (C) 2008 Florian octo Forster\n"
1565             "\n"
1566             "Usage: rrdcached [options]\n"
1567             "\n"
1568             "Valid options are:\n"
1569             "  -l <address>  Socket address to listen to.\n"
1570             "  -w <seconds>  Interval in which to write data.\n"
1571             "  -f <seconds>  Interval in which to flush dead data.\n"
1572             "  -p <file>     Location of the PID-file.\n"
1573             "  -b <dir>      Base directory to change to.\n"
1574             "\n"
1575             "For more information and a detailed description of all options "
1576             "please refer\n"
1577             "to the rrdcached(1) manual page.\n",
1578             VERSION);
1579         status = -1;
1580         break;
1581     } /* switch (option) */
1582   } /* while (getopt) */
1583
1584   return (status);
1585 } /* }}} int read_options */
1586
1587 int main (int argc, char **argv)
1588 {
1589   int status;
1590
1591   status = read_options (argc, argv);
1592   if (status != 0)
1593   {
1594     if (status < 0)
1595       status = 0;
1596     return (status);
1597   }
1598
1599   status = daemonize ();
1600   if (status == 1)
1601   {
1602     struct sigaction sigchld;
1603
1604     memset (&sigchld, 0, sizeof (sigchld));
1605     sigchld.sa_handler = SIG_IGN;
1606     sigaction (SIGCHLD, &sigchld, NULL);
1607
1608     return (0);
1609   }
1610   else if (status != 0)
1611   {
1612     fprintf (stderr, "daemonize failed, exiting.\n");
1613     return (1);
1614   }
1615
1616   listen_thread_main (NULL);
1617
1618   cleanup ();
1619
1620   return (0);
1621 } /* int main */
1622
1623 /*
1624  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
1625  */