Merge branch 'ff/types' into collectd-4
[collectd.git] / src / unixsock.c
1 /**
2  * collectd - src/unixsock.c
3  * Copyright (C) 2007  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  * Author:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26
27 /* Folks without pthread will need to disable this plugin. */
28 #include <pthread.h>
29
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <sys/poll.h>
33
34 #include <grp.h>
35
36 #ifndef UNIX_PATH_MAX
37 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
38 #endif
39
40 #define US_DEFAULT_PATH PREFIX"/var/run/"PACKAGE_NAME"-unixsock"
41
42 /*
43  * Private data structures
44  */
45 /* linked list of cached values */
46 typedef struct value_cache_s
47 {
48         char       name[4*DATA_MAX_NAME_LEN];
49         int        values_num;
50         gauge_t   *gauge;
51         counter_t *counter;
52         const data_set_t *ds;
53         time_t     time;
54         struct value_cache_s *next;
55 } value_cache_t;
56
57 /*
58  * Private variables
59  */
60 /* valid configuration file keys */
61 static const char *config_keys[] =
62 {
63         "SocketFile",
64         "SocketGroup",
65         "SocketPerms",
66         NULL
67 };
68 static int config_keys_num = 3;
69
70 static int loop = 0;
71
72 /* socket configuration */
73 static int   sock_fd    = -1;
74 static char *sock_file  = NULL;
75 static char *sock_group = NULL;
76 static int   sock_perms = S_IRWXU | S_IRWXG;
77
78 static pthread_t listen_thread = (pthread_t) 0;
79
80 /* Linked list and auxilliary variables for saving values */
81 static value_cache_t   *cache_head = NULL;
82 static pthread_mutex_t  cache_lock = PTHREAD_MUTEX_INITIALIZER;
83 static unsigned int     cache_oldest = UINT_MAX;
84
85 /*
86  * Functions
87  */
88 static int parse_identifier (char *str, char **ret_host,
89                 char **ret_plugin, char **ret_plugin_instance,
90                 char **ret_type, char **ret_type_instance)
91 {
92         char *hostname = NULL;
93         char *plugin = NULL;
94         char *plugin_instance = NULL;
95         char *type = NULL;
96         char *type_instance = NULL;
97
98         hostname = str;
99         if (hostname == NULL)
100                 return (-1);
101
102         plugin = strchr (hostname, '/');
103         if (plugin == NULL)
104                 return (-1);
105         *plugin = '\0'; plugin++;
106
107         type = strchr (plugin, '/');
108         if (type == NULL)
109                 return (-1);
110         *type = '\0'; type++;
111
112         plugin_instance = strchr (plugin, '-');
113         if (plugin_instance != NULL)
114         {
115                 *plugin_instance = '\0';
116                 plugin_instance++;
117         }
118
119         type_instance = strchr (type, '-');
120         if (type_instance != NULL)
121         {
122                 *type_instance = '\0';
123                 type_instance++;
124         }
125
126         *ret_host = hostname;
127         *ret_plugin = plugin;
128         *ret_plugin_instance = plugin_instance;
129         *ret_type = type;
130         *ret_type_instance = type_instance;
131         return (0);
132 } /* int parse_identifier */
133
134 static value_cache_t *cache_search (const char *name)
135 {
136         value_cache_t *vc;
137
138         for (vc = cache_head; vc != NULL; vc = vc->next)
139         {
140                 if (strcmp (vc->name, name) == 0)
141                         break;
142         } /* for vc = cache_head .. NULL */
143
144         return (vc);
145 } /* value_cache_t *cache_search */
146
147 static int cache_insert (const data_set_t *ds, const value_list_t *vl)
148 {
149         /* We're called from `cache_update' so we don't need to lock the mutex */
150         value_cache_t *vc;
151         int i;
152
153         DEBUG ("unixsock plugin: cache_insert: ds->ds_num = %i;"
154                         " vl->values_len = %i;",
155                         ds->ds_num, vl->values_len);
156         assert (ds->ds_num == vl->values_len);
157
158         vc = (value_cache_t *) malloc (sizeof (value_cache_t));
159         if (vc == NULL)
160         {
161                 char errbuf[1024];
162                 pthread_mutex_unlock (&cache_lock);
163                 ERROR ("unixsock plugin: malloc failed: %s",
164                                 sstrerror (errno, errbuf, sizeof (errbuf)));
165                 return (-1);
166         }
167
168         vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len);
169         if (vc->gauge == NULL)
170         {
171                 char errbuf[1024];
172                 pthread_mutex_unlock (&cache_lock);
173                 ERROR ("unixsock plugin: malloc failed: %s",
174                                 sstrerror (errno, errbuf, sizeof (errbuf)));
175                 free (vc);
176                 return (-1);
177         }
178
179         vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len);
180         if (vc->counter == NULL)
181         {
182                 char errbuf[1024];
183                 pthread_mutex_unlock (&cache_lock);
184                 ERROR ("unixsock plugin: malloc failed: %s",
185                                 sstrerror (errno, errbuf, sizeof (errbuf)));
186                 free (vc->gauge);
187                 free (vc);
188                 return (-1);
189         }
190
191         if (FORMAT_VL (vc->name, sizeof (vc->name), vl, ds))
192         {
193                 pthread_mutex_unlock (&cache_lock);
194                 ERROR ("unixsock plugin: FORMAT_VL failed.");
195                 free (vc->counter);
196                 free (vc->gauge);
197                 free (vc);
198                 return (-1);
199         }
200
201         for (i = 0; i < ds->ds_num; i++)
202         {
203                 if (ds->ds[i].type == DS_TYPE_COUNTER)
204                 {
205                         vc->gauge[i] = 0.0;
206                         vc->counter[i] = vl->values[i].counter;
207                 }
208                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
209                 {
210                         vc->gauge[i] = vl->values[i].gauge;
211                         vc->counter[i] = 0;
212                 }
213                 else
214                 {
215                         vc->gauge[i] = 0.0;
216                         vc->counter[i] = 0;
217                 }
218         }
219         vc->values_num = ds->ds_num;
220         vc->ds = ds;
221
222         vc->next = cache_head;
223         cache_head = vc;
224
225         vc->time = vl->time;
226         if (vc->time < cache_oldest)
227                 cache_oldest = vc->time;
228
229         pthread_mutex_unlock (&cache_lock);
230         return (0);
231 } /* int cache_insert */
232
233 static int cache_update (const data_set_t *ds, const value_list_t *vl)
234 {
235         char name[4*DATA_MAX_NAME_LEN];;
236         value_cache_t *vc;
237         int i;
238
239         if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
240                 return (-1);
241
242         pthread_mutex_lock (&cache_lock);
243
244         vc = cache_search (name);
245
246         /* pthread_mutex_lock is called by cache_insert. */
247         if (vc == NULL)
248                 return (cache_insert (ds, vl));
249
250         assert (vc->values_num == ds->ds_num);
251         assert (vc->values_num == vl->values_len);
252
253         /* Avoid floating-point exceptions due to division by zero. */
254         if (vc->time >= vl->time)
255         {
256                 pthread_mutex_unlock (&cache_lock);
257                 ERROR ("unixsock plugin: vc->time >= vl->time. vc->time = %u; "
258                                 "vl->time = %u; vl = %s;",
259                                 (unsigned int) vc->time, (unsigned int) vl->time,
260                                 name);
261                 return (-1);
262         } /* if (vc->time >= vl->time) */
263
264         /*
265          * Update the values. This is possibly a lot more that you'd expect
266          * because we honor min and max values and handle counter overflows here.
267          */
268         for (i = 0; i < ds->ds_num; i++)
269         {
270                 if (ds->ds[i].type == DS_TYPE_COUNTER)
271                 {
272                         if (vl->values[i].counter < vc->counter[i])
273                         {
274                                 if (vl->values[i].counter <= 4294967295U)
275                                 {
276                                         vc->gauge[i] = ((4294967295U - vl->values[i].counter)
277                                                         + vc->counter[i]) / (vl->time - vc->time);
278                                 }
279                                 else
280                                 {
281                                         vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter)
282                                                 + vc->counter[i]) / (vl->time - vc->time);
283                                 }
284                         }
285                         else
286                         {
287                                 vc->gauge[i] = (vl->values[i].counter - vc->counter[i])
288                                         / (vl->time - vc->time);
289                         }
290
291                         vc->counter[i] = vl->values[i].counter;
292                 }
293                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
294                 {
295                         vc->gauge[i] = vl->values[i].gauge;
296                         vc->counter[i] = 0;
297                 }
298                 else
299                 {
300                         vc->gauge[i] = NAN;
301                         vc->counter[i] = 0;
302                 }
303
304                 if (isnan (vc->gauge[i])
305                                 || (!isnan (ds->ds[i].min) && (vc->gauge[i] < ds->ds[i].min))
306                                 || (!isnan (ds->ds[i].max) && (vc->gauge[i] > ds->ds[i].max)))
307                         vc->gauge[i] = NAN;
308         } /* for i = 0 .. ds->ds_num */
309
310         vc->ds = ds;
311         vc->time = vl->time;
312
313         if (vc->time < cache_oldest)
314                 cache_oldest = vc->time;
315
316         pthread_mutex_unlock (&cache_lock);
317         return (0);
318 } /* int cache_update */
319
320 static void cache_flush (int max_age)
321 {
322         value_cache_t *this;
323         value_cache_t *prev;
324         time_t now;
325
326         pthread_mutex_lock (&cache_lock);
327
328         now = time (NULL);
329
330         if ((now - cache_oldest) <= max_age)
331         {
332                 pthread_mutex_unlock (&cache_lock);
333                 return;
334         }
335         
336         cache_oldest = now;
337
338         prev = NULL;
339         this = cache_head;
340
341         while (this != NULL)
342         {
343                 if ((now - this->time) <= max_age)
344                 {
345                         if (this->time < cache_oldest)
346                                 cache_oldest = this->time;
347
348                         prev = this;
349                         this = this->next;
350                         continue;
351                 }
352
353                 if (prev == NULL)
354                         cache_head = this->next;
355                 else
356                         prev->next = this->next;
357
358                 free (this->gauge);
359                 free (this->counter);
360                 free (this);
361
362                 if (prev == NULL)
363                         this = cache_head;
364                 else
365                         this = prev->next;
366         } /* while (this != NULL) */
367
368         pthread_mutex_unlock (&cache_lock);
369 } /* int cache_flush */
370
371 static int us_open_socket (void)
372 {
373         struct sockaddr_un sa;
374         int status;
375
376         sock_fd = socket (PF_UNIX, SOCK_STREAM, 0);
377         if (sock_fd < 0)
378         {
379                 char errbuf[1024];
380                 ERROR ("unixsock plugin: socket failed: %s",
381                                 sstrerror (errno, errbuf, sizeof (errbuf)));
382                 return (-1);
383         }
384
385         memset (&sa, '\0', sizeof (sa));
386         sa.sun_family = AF_UNIX;
387         strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
388                         sizeof (sa.sun_path) - 1);
389         /* unlink (sa.sun_path); */
390
391         status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa));
392         if (status != 0)
393         {
394                 char errbuf[1024];
395                 sstrerror (errno, errbuf, sizeof (errbuf));
396                 DEBUG ("bind failed: %s; sa.sun_path = %s", errbuf, sa.sun_path);
397                 ERROR ("unixsock plugin: bind failed: %s", errbuf);
398                 close (sock_fd);
399                 sock_fd = -1;
400                 return (-1);
401         }
402
403         status = listen (sock_fd, 8);
404         if (status != 0)
405         {
406                 char errbuf[1024];
407                 ERROR ("unixsock plugin: listen failed: %s",
408                                 sstrerror (errno, errbuf, sizeof (errbuf)));
409                 close (sock_fd);
410                 sock_fd = -1;
411                 return (-1);
412         }
413
414         do
415         {
416                 char *grpname;
417                 struct group *g;
418                 struct group sg;
419                 char grbuf[2048];
420
421                 grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME;
422                 g = NULL;
423
424                 status = getgrnam_r (grpname, &sg, grbuf, sizeof (grbuf), &g);
425                 if (status != 0)
426                 {
427                         char errbuf[1024];
428                         WARNING ("unixsock plugin: getgrnam_r (%s) failed: %s", grpname,
429                                         sstrerror (errno, errbuf, sizeof (errbuf)));
430                         break;
431                 }
432                 if (g == NULL)
433                 {
434                         WARNING ("unixsock plugin: No such group: `%s'",
435                                         grpname);
436                         break;
437                 }
438
439                 if (chown ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
440                                         (uid_t) -1, g->gr_gid) != 0)
441                 {
442                         char errbuf[1024];
443                         WARNING ("unixsock plugin: chown (%s, -1, %i) failed: %s",
444                                         (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
445                                         (int) g->gr_gid,
446                                         sstrerror (errno, errbuf, sizeof (errbuf)));
447                 }
448         } while (0);
449
450         return (0);
451 } /* int us_open_socket */
452
453 static int us_handle_getval (FILE *fh, char **fields, int fields_num)
454 {
455         char *hostname;
456         char *plugin;
457         char *plugin_instance;
458         char *type;
459         char *type_instance;
460         char  name[4*DATA_MAX_NAME_LEN];
461         value_cache_t *vc;
462         int   status;
463         int   i;
464
465         if (fields_num != 2)
466         {
467                 DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
468                 fprintf (fh, "-1 Wrong number of fields: Got %i, expected 2.\n",
469                                 fields_num);
470                 fflush (fh);
471                 return (-1);
472         }
473         DEBUG ("unixsock plugin: Got query for `%s'", fields[1]);
474
475         status = parse_identifier (fields[1], &hostname,
476                         &plugin, &plugin_instance,
477                         &type, &type_instance);
478         if (status != 0)
479         {
480                 DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
481                 fprintf (fh, "-1 Cannot parse identifier.\n");
482                 fflush (fh);
483                 return (-1);
484         }
485
486         status = format_name (name, sizeof (name),
487                         hostname, plugin, plugin_instance, type, type_instance);
488         /* FIXME: Send some response */
489         if (status != 0)
490                 return (-1);
491
492         pthread_mutex_lock (&cache_lock);
493
494         DEBUG ("vc = cache_search (%s)", name);
495         vc = cache_search (name);
496
497         if (vc == NULL)
498         {
499                 DEBUG ("Did not find cache entry.");
500                 fprintf (fh, "-1 No such value");
501         }
502         else
503         {
504                 DEBUG ("Found cache entry.");
505                 fprintf (fh, "%i", vc->values_num);
506                 for (i = 0; i < vc->values_num; i++)
507                 {
508                         fprintf (fh, " %s=", vc->ds->ds[i].name);
509                         if (isnan (vc->gauge[i]))
510                                 fprintf (fh, "NaN");
511                         else
512                                 fprintf (fh, "%12e", vc->gauge[i]);
513                 }
514         }
515
516         /* Free the mutex as soon as possible and definitely before flushing */
517         pthread_mutex_unlock (&cache_lock);
518
519         fprintf (fh, "\n");
520         fflush (fh);
521
522         return (0);
523 } /* int us_handle_getval */
524
525 static int us_handle_putval (FILE *fh, char **fields, int fields_num)
526 {
527         char *hostname;
528         char *plugin;
529         char *plugin_instance;
530         char *type;
531         char *type_instance;
532         int   status;
533         int   i;
534
535         const data_set_t *ds;
536         value_list_t vl = VALUE_LIST_INIT;
537
538         char **value_ptr;
539
540         if (fields_num != 3)
541         {
542                 DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
543                 fprintf (fh, "-1 Wrong number of fields: Got %i, expected 3.\n",
544                                 fields_num);
545                 fflush (fh);
546                 return (-1);
547         }
548
549         status = parse_identifier (fields[1], &hostname,
550                         &plugin, &plugin_instance,
551                         &type, &type_instance);
552         if (status != 0)
553         {
554                 DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
555                 fprintf (fh, "-1 Cannot parse identifier.\n");
556                 fflush (fh);
557                 return (-1);
558         }
559
560         /* FIXME: Send some response */
561         if ((strlen (hostname) > sizeof (vl.host))
562                         || (strlen (plugin) > sizeof (vl.plugin))
563                         || ((plugin_instance != NULL)
564                                 && (strlen (plugin_instance) > sizeof (vl.plugin_instance)))
565                         || ((type_instance != NULL)
566                                 && (strlen (type_instance) > sizeof (vl.type_instance))))
567                 return (-1);
568
569         strcpy (vl.host, hostname);
570         strcpy (vl.plugin, plugin);
571         if (plugin_instance != NULL)
572                 strcpy (vl.plugin_instance, plugin_instance);
573         if (type_instance != NULL)
574                 strcpy (vl.type_instance, type_instance);
575
576         { /* parse the time */
577                 char *t = fields[2];
578                 char *v = strchr (t, ':');
579                 if (v == NULL)
580                         return (-1);
581                 *v = '\0'; v++;
582
583                 vl.time = (time_t) atoi (t);
584                 if (vl.time == 0)
585                         vl.time = time (NULL);
586
587                 fields[2] = v;
588         }
589
590         ds = plugin_get_ds (type);
591         if (ds == NULL)
592                 return (-1);
593
594         value_ptr = (char **) calloc (ds->ds_num, sizeof (char *));
595         /* FIXME: Send some response */
596         if (value_ptr == NULL)
597                 return (-1);
598
599
600         { /* parse the value-list. It's colon-separated. */
601                 char *dummy;
602                 char *ptr;
603                 char *saveptr;
604
605                 i = 0;
606                 dummy = fields[2];
607                 saveptr = NULL;
608                 while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
609                 {
610                         dummy = NULL;
611                         if (i >= ds->ds_num)
612                         {
613                                 i = ds->ds_num + 1;
614                                 break;
615                         }
616                         value_ptr[i] = ptr;
617                         i++;
618                 }
619
620                 if (i != ds->ds_num)
621                 {
622                         sfree (value_ptr);
623                         /* FIXME: Send some response */
624                         return (-1);
625                 }
626         } /* done parsing the value-list */
627
628         vl.values_len = ds->ds_num;
629         vl.values = (value_t *) malloc (vl.values_len * sizeof (value_t));
630         if (vl.values == NULL)
631         {
632                 sfree (value_ptr);
633                 return (-1);
634         }
635         DEBUG ("value_ptr = 0x%p; vl.values = 0x%p;", (void *) value_ptr, (void *) vl.values);
636
637         for (i = 0; i < ds->ds_num; i++)
638         {
639                 if (strcmp (value_ptr[i], "U") == 0)
640                         vl.values[i].gauge = NAN;
641                 else if (ds->ds[i].type == DS_TYPE_COUNTER)
642                         vl.values[i].counter = atoll (value_ptr[i]);
643                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
644                         vl.values[i].gauge = atof (value_ptr[i]);
645         } /* for (i = 2 .. fields_num) */
646
647         plugin_dispatch_values (type, &vl);
648
649         DEBUG ("value_ptr = 0x%p; vl.values = 0x%p;", (void *) value_ptr, (void *) vl.values);
650
651         sfree (value_ptr);
652         sfree (vl.values); 
653
654         fprintf (fh, "0 Success\n");
655         fflush (fh);
656
657         return (0);
658 } /* int us_handle_putval */
659
660 static void *us_handle_client (void *arg)
661 {
662         int fd;
663         FILE *fh;
664         char buffer[1024];
665         char *fields[128];
666         int   fields_num;
667
668         fd = *((int *) arg);
669         free (arg);
670         arg = NULL;
671
672         DEBUG ("Reading from fd #%i", fd);
673
674         fh = fdopen (fd, "r+");
675         if (fh == NULL)
676         {
677                 char errbuf[1024];
678                 ERROR ("unixsock plugin: fdopen failed: %s",
679                                 sstrerror (errno, errbuf, sizeof (errbuf)));
680                 close (fd);
681                 pthread_exit ((void *) 1);
682         }
683
684         while (fgets (buffer, sizeof (buffer), fh) != NULL)
685         {
686                 int len;
687
688                 len = strlen (buffer);
689                 while ((len > 0)
690                                 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
691                         buffer[--len] = '\0';
692
693                 if (len == 0)
694                         continue;
695
696                 DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
697
698                 fields_num = strsplit (buffer, fields,
699                                 sizeof (fields) / sizeof (fields[0]));
700
701                 if (fields_num < 1)
702                 {
703                         close (fd);
704                         break;
705                 }
706
707                 if (strcasecmp (fields[0], "getval") == 0)
708                 {
709                         us_handle_getval (fh, fields, fields_num);
710                 }
711                 else if (strcasecmp (fields[0], "putval") == 0)
712                 {
713                         us_handle_putval (fh, fields, fields_num);
714                 }
715                 else
716                 {
717                         fprintf (fh, "Unknown command: %s\n", fields[0]);
718                         fflush (fh);
719                 }
720         } /* while (fgets) */
721
722         DEBUG ("Exiting..");
723         close (fd);
724
725         pthread_exit ((void *) 0);
726 } /* void *us_handle_client */
727
728 static void *us_server_thread (void *arg)
729 {
730         int  status;
731         int *remote_fd;
732         pthread_t th;
733         pthread_attr_t th_attr;
734
735         if (us_open_socket () != 0)
736                 pthread_exit ((void *) 1);
737
738         while (loop != 0)
739         {
740                 DEBUG ("Calling accept..");
741                 status = accept (sock_fd, NULL, NULL);
742                 if (status < 0)
743                 {
744                         char errbuf[1024];
745
746                         if (errno == EINTR)
747                                 continue;
748
749                         ERROR ("unixsock plugin: accept failed: %s",
750                                         sstrerror (errno, errbuf, sizeof (errbuf)));
751                         close (sock_fd);
752                         sock_fd = -1;
753                         pthread_exit ((void *) 1);
754                 }
755
756                 remote_fd = (int *) malloc (sizeof (int));
757                 if (remote_fd == NULL)
758                 {
759                         char errbuf[1024];
760                         WARNING ("unixsock plugin: malloc failed: %s",
761                                         sstrerror (errno, errbuf, sizeof (errbuf)));
762                         close (status);
763                         continue;
764                 }
765                 *remote_fd = status;
766
767                 DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
768
769                 pthread_attr_init (&th_attr);
770                 pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
771
772                 status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
773                 if (status != 0)
774                 {
775                         char errbuf[1024];
776                         WARNING ("unixsock plugin: pthread_create failed: %s",
777                                         sstrerror (errno, errbuf, sizeof (errbuf)));
778                         close (*remote_fd);
779                         free (remote_fd);
780                         continue;
781                 }
782         } /* while (loop) */
783
784         close (sock_fd);
785         sock_fd = -1;
786
787         return ((void *) 0);
788 } /* void *us_server_thread */
789
790 static int us_config (const char *key, const char *val)
791 {
792         if (strcasecmp (key, "SocketFile") == 0)
793         {
794                 sfree (sock_file);
795                 sock_file = strdup (val);
796         }
797         else if (strcasecmp (key, "SocketGroup") == 0)
798         {
799                 sfree (sock_group);
800                 sock_group = strdup (val);
801         }
802         else if (strcasecmp (key, "SocketPerms") == 0)
803         {
804                 sock_perms = (int) strtol (val, NULL, 8);
805         }
806         else
807         {
808                 return (-1);
809         }
810
811         return (0);
812 } /* int us_config */
813
814 static int us_init (void)
815 {
816         int status;
817
818         loop = 1;
819
820         status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
821         if (status != 0)
822         {
823                 char errbuf[1024];
824                 ERROR ("unixsock plugin: pthread_create failed: %s",
825                                 sstrerror (errno, errbuf, sizeof (errbuf)));
826                 return (-1);
827         }
828
829         return (0);
830 } /* int us_init */
831
832 static int us_shutdown (void)
833 {
834         void *ret;
835
836         loop = 0;
837
838         if (listen_thread != (pthread_t) 0)
839         {
840                 pthread_kill (listen_thread, SIGTERM);
841                 pthread_join (listen_thread, &ret);
842                 listen_thread = (pthread_t) 0;
843         }
844
845         plugin_unregister_init ("unixsock");
846         plugin_unregister_write ("unixsock");
847         plugin_unregister_shutdown ("unixsock");
848
849         return (0);
850 } /* int us_shutdown */
851
852 static int us_write (const data_set_t *ds, const value_list_t *vl)
853 {
854         cache_update (ds, vl);
855         cache_flush (2 * interval_g);
856
857         return (0);
858 }
859
860 void module_register (void)
861 {
862         plugin_register_config ("unixsock", us_config,
863                         config_keys, config_keys_num);
864         plugin_register_init ("unixsock", us_init);
865         plugin_register_write ("unixsock", us_write);
866         plugin_register_shutdown ("unixsock", us_shutdown);
867 } /* void module_register (void) */
868
869 /* vim: set sw=4 ts=4 sts=4 tw=78 : */