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