Code

Replace all calls to `strerror' with `sstrerror'
[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  **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
27 /* Folks without pthread will need to disable this plugin. */
28 #include <pthread.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <sys/poll.h>
34 #include <grp.h>
36 #ifndef UNIX_PATH_MAX
37 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
38 #endif
40 #define US_DEFAULT_PATH PREFIX"/var/run/"PACKAGE_NAME"-unixsock"
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;
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;
70 /* socket configuration */
71 static int   sock_fd    = -1;
72 static char *sock_file  = NULL;
73 static char *sock_group = NULL;
74 static int   sock_perms = S_IRWXU | S_IRWXG;
76 static pthread_t listen_thread = (pthread_t) 0;
78 /* Linked list and auxilliary variables for saving values */
79 static value_cache_t   *cache_head = NULL;
80 static pthread_mutex_t  cache_lock = PTHREAD_MUTEX_INITIALIZER;
81 static unsigned int     cache_oldest = UINT_MAX;
83 /*
84  * Functions
85  */
86 static int parse_identifier (char *str, char **ret_host,
87                 char **ret_plugin, char **ret_plugin_instance,
88                 char **ret_type, char **ret_type_instance)
89 {
90         char *hostname = NULL;
91         char *plugin = NULL;
92         char *plugin_instance = NULL;
93         char *type = NULL;
94         char *type_instance = NULL;
96         hostname = str;
97         if (hostname == NULL)
98                 return (-1);
100         plugin = strchr (hostname, '/');
101         if (plugin == NULL)
102                 return (-1);
103         *plugin = '\0'; plugin++;
105         type = strchr (plugin, '/');
106         if (type == NULL)
107                 return (-1);
108         *type = '\0'; type++;
110         plugin_instance = strchr (plugin, '-');
111         if (plugin_instance != NULL)
112         {
113                 *plugin_instance = '\0';
114                 plugin_instance++;
115         }
117         type_instance = strchr (type, '-');
118         if (type_instance != NULL)
119         {
120                 *type_instance = '\0';
121                 type_instance++;
122         }
124         *ret_host = hostname;
125         *ret_plugin = plugin;
126         *ret_plugin_instance = plugin_instance;
127         *ret_type = type;
128         *ret_type_instance = type_instance;
129         return (0);
130 } /* int parse_identifier */
132 static value_cache_t *cache_search (const char *name)
134         value_cache_t *vc;
136         for (vc = cache_head; vc != NULL; vc = vc->next)
137         {
138                 if (strcmp (vc->name, name) == 0)
139                         break;
140         } /* for vc = cache_head .. NULL */
142         return (vc);
143 } /* value_cache_t *cache_search */
145 static int cache_alloc_name (char *ret, int ret_len,
146                 const char *hostname,
147                 const char *plugin, const char *plugin_instance,
148                 const char *type, const char *type_instance)
150         int  status;
152         assert (plugin != NULL);
153         assert (type != NULL);
155         if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
156         {
157                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
158                         status = snprintf (ret, ret_len, "%s/%s/%s",
159                                         hostname, plugin, type);
160                 else
161                         status = snprintf (ret, ret_len, "%s/%s/%s-%s",
162                                         hostname, plugin, type, type_instance);
163         }
164         else
165         {
166                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
167                         status = snprintf (ret, ret_len, "%s/%s-%s/%s",
168                                         hostname, plugin, plugin_instance, type);
169                 else
170                         status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s",
171                                         hostname, plugin, plugin_instance, type, type_instance);
172         }
174         if ((status < 1) || (status >= ret_len))
175                 return (-1);
176         return (0);
177 } /* int cache_alloc_name */
179 static int cache_insert (const data_set_t *ds, const value_list_t *vl)
181         /* We're called from `cache_update' so we don't need to lock the mutex */
182         value_cache_t *vc;
183         int i;
185         DEBUG ("ds->ds_num = %i; vl->values_len = %i;",
186                         ds->ds_num, vl->values_len);
187         assert (ds->ds_num == vl->values_len);
189         vc = (value_cache_t *) malloc (sizeof (value_cache_t));
190         if (vc == NULL)
191         {
192                 char errbuf[1024];
193                 pthread_mutex_unlock (&cache_lock);
194                 ERROR ("unixsock plugin: malloc failed: %s",
195                                 sstrerror (errno, errbuf, sizeof (errbuf)));
196                 return (-1);
197         }
199         vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len);
200         if (vc->gauge == NULL)
201         {
202                 char errbuf[1024];
203                 pthread_mutex_unlock (&cache_lock);
204                 ERROR ("unixsock plugin: malloc failed: %s",
205                                 sstrerror (errno, errbuf, sizeof (errbuf)));
206                 free (vc);
207                 return (-1);
208         }
210         vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len);
211         if (vc->counter == NULL)
212         {
213                 char errbuf[1024];
214                 pthread_mutex_unlock (&cache_lock);
215                 ERROR ("unixsock plugin: malloc failed: %s",
216                                 sstrerror (errno, errbuf, sizeof (errbuf)));
217                 free (vc->gauge);
218                 free (vc);
219                 return (-1);
220         }
222         if (cache_alloc_name (vc->name, sizeof (vc->name),
223                                 vl->host, vl->plugin, vl->plugin_instance,
224                                 ds->type, vl->type_instance) != 0)
225         {
226                 pthread_mutex_unlock (&cache_lock);
227                 ERROR ("unixsock plugin: cache_alloc_name failed.");
228                 free (vc->counter);
229                 free (vc->gauge);
230                 free (vc);
231                 return (-1);
232         }
234         for (i = 0; i < ds->ds_num; i++)
235         {
236                 if (ds->ds[i].type == DS_TYPE_COUNTER)
237                 {
238                         vc->gauge[i] = 0.0;
239                         vc->counter[i] = vl->values[i].counter;
240                 }
241                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
242                 {
243                         vc->gauge[i] = vl->values[i].gauge;
244                         vc->counter[i] = 0;
245                 }
246                 else
247                 {
248                         vc->gauge[i] = 0.0;
249                         vc->counter[i] = 0;
250                 }
251         }
252         vc->values_num = ds->ds_num;
253         vc->ds = ds;
255         vc->next = cache_head;
256         cache_head = vc;
258         vc->time = vl->time;
259         if (vc->time < cache_oldest)
260                 cache_oldest = vc->time;
262         pthread_mutex_unlock (&cache_lock);
263         return (0);
264 } /* int cache_insert */
266 static int cache_update (const data_set_t *ds, const value_list_t *vl)
268         char name[4*DATA_MAX_NAME_LEN];;
269         value_cache_t *vc;
270         int i;
272         if (cache_alloc_name (name, sizeof (name),
273                                 vl->host,
274                                 vl->plugin, vl->plugin_instance,
275                                 ds->type, vl->type_instance) != 0)
276                 return (-1);
278         pthread_mutex_lock (&cache_lock);
280         vc = cache_search (name);
282         if (vc == NULL)
283                 return (cache_insert (ds, vl));
285         assert (vc->values_num == ds->ds_num);
286         assert (vc->values_num == vl->values_len);
288         /*
289          * Update the values. This is possibly a lot more that you'd expect
290          * because we honor min and max values and handle counter overflows here.
291          */
292         for (i = 0; i < ds->ds_num; i++)
293         {
294                 if (ds->ds[i].type == DS_TYPE_COUNTER)
295                 {
296                         if (vl->values[i].counter < vc->counter[i])
297                         {
298                                 if (vl->values[i].counter <= 4294967295U)
299                                 {
300                                         vc->gauge[i] = ((4294967295U - vl->values[i].counter)
301                                                         + vc->counter[i]) / (vl->time - vc->time);
302                                 }
303                                 else
304                                 {
305                                         vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter)
306                                                 + vc->counter[i]) / (vl->time - vc->time);
307                                 }
308                         }
309                         else
310                         {
311                                 vc->gauge[i] = (vl->values[i].counter - vc->counter[i])
312                                         / (vl->time - vc->time);
313                         }
315                         vc->counter[i] = vl->values[i].counter;
316                 }
317                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
318                 {
319                         vc->gauge[i] = vl->values[i].gauge;
320                         vc->counter[i] = 0;
321                 }
322                 else
323                 {
324                         vc->gauge[i] = NAN;
325                         vc->counter[i] = 0;
326                 }
328                 if (isnan (vc->gauge[i])
329                                 || (!isnan (ds->ds[i].min) && (vc->gauge[i] < ds->ds[i].min))
330                                 || (!isnan (ds->ds[i].max) && (vc->gauge[i] > ds->ds[i].max)))
331                         vc->gauge[i] = NAN;
332         } /* for i = 0 .. ds->ds_num */
334         vc->ds = ds;
335         vc->time = vl->time;
337         if (vc->time < cache_oldest)
338                 cache_oldest = vc->time;
340         pthread_mutex_unlock (&cache_lock);
341         return (0);
342 } /* int cache_update */
344 static void cache_flush (int max_age)
346         value_cache_t *this;
347         value_cache_t *prev;
348         time_t now;
350         pthread_mutex_lock (&cache_lock);
352         now = time (NULL);
354         if ((now - cache_oldest) <= max_age)
355         {
356                 pthread_mutex_unlock (&cache_lock);
357                 return;
358         }
359         
360         cache_oldest = now;
362         prev = NULL;
363         this = cache_head;
365         while (this != NULL)
366         {
367                 if ((now - this->time) <= max_age)
368                 {
369                         if (this->time < cache_oldest)
370                                 cache_oldest = this->time;
372                         prev = this;
373                         this = this->next;
374                         continue;
375                 }
377                 if (prev == NULL)
378                         cache_head = this->next;
379                 else
380                         prev->next = this->next;
382                 free (this->gauge);
383                 free (this->counter);
384                 free (this);
386                 if (prev == NULL)
387                         this = cache_head;
388                 else
389                         this = prev->next;
390         } /* while (this != NULL) */
392         pthread_mutex_unlock (&cache_lock);
393 } /* int cache_flush */
395 static int us_open_socket (void)
397         struct sockaddr_un sa;
398         int status;
400         sock_fd = socket (PF_UNIX, SOCK_STREAM, 0);
401         if (sock_fd < 0)
402         {
403                 char errbuf[1024];
404                 ERROR ("unixsock plugin: socket failed: %s",
405                                 sstrerror (errno, errbuf, sizeof (errbuf)));
406                 return (-1);
407         }
409         memset (&sa, '\0', sizeof (sa));
410         sa.sun_family = AF_UNIX;
411         strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
412                         sizeof (sa.sun_path) - 1);
413         /* unlink (sa.sun_path); */
415         status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa));
416         if (status != 0)
417         {
418                 char errbuf[1024];
419                 sstrerror (errno, errbuf, sizeof (errbuf));
420                 DEBUG ("bind failed: %s; sa.sun_path = %s", errbuf, sa.sun_path);
421                 ERROR ("unixsock plugin: bind failed: %s", errbuf);
422                 close (sock_fd);
423                 sock_fd = -1;
424                 return (-1);
425         }
427         status = listen (sock_fd, 8);
428         if (status != 0)
429         {
430                 char errbuf[1024];
431                 ERROR ("unixsock plugin: listen failed: %s",
432                                 sstrerror (errno, errbuf, sizeof (errbuf)));
433                 close (sock_fd);
434                 sock_fd = -1;
435                 return (-1);
436         }
438         do
439         {
440                 char *grpname;
441                 struct group *g;
442                 struct group sg;
443                 char grbuf[2048];
445                 grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME;
446                 g = NULL;
448                 status = getgrnam_r (grpname, &sg, grbuf, sizeof (grbuf), &g);
449                 if (status != 0)
450                 {
451                         char errbuf[1024];
452                         WARNING ("unixsock plugin: getgrnam_r (%s) failed: %s", grpname,
453                                         sstrerror (errno, errbuf, sizeof (errbuf)));
454                         break;
455                 }
456                 if (g == NULL)
457                 {
458                         WARNING ("unixsock plugin: No such group: `%s'",
459                                         grpname);
460                         break;
461                 }
463                 if (chown ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
464                                         (uid_t) -1, g->gr_gid) != 0)
465                 {
466                         char errbuf[1024];
467                         WARNING ("unixsock plugin: chown (%s, -1, %i) failed: %s",
468                                         (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
469                                         (int) g->gr_gid,
470                                         sstrerror (errno, errbuf, sizeof (errbuf)));
471                 }
472         } while (0);
474         return (0);
475 } /* int us_open_socket */
477 static int us_handle_getval (FILE *fh, char **fields, int fields_num)
479         char *hostname;
480         char *plugin;
481         char *plugin_instance;
482         char *type;
483         char *type_instance;
484         char  name[4*DATA_MAX_NAME_LEN];
485         value_cache_t *vc;
486         int   status;
487         int   i;
489         if (fields_num != 2)
490                 return (-1);
492         status = parse_identifier (fields[1], &hostname,
493                         &plugin, &plugin_instance,
494                         &type, &type_instance);
495         if (status != 0)
496                 return (-1);
498         status = cache_alloc_name (name, sizeof (name),
499                         hostname, plugin, plugin_instance, type, type_instance);
500         if (status != 0)
501                 return (-1);
503         pthread_mutex_lock (&cache_lock);
505         DEBUG ("vc = cache_search (%s)", name);
506         vc = cache_search (name);
508         if (vc == NULL)
509         {
510                 DEBUG ("Did not find cache entry.");
511                 fprintf (fh, "-1 No such value");
512         }
513         else
514         {
515                 DEBUG ("Found cache entry.");
516                 fprintf (fh, "%i", vc->values_num);
517                 for (i = 0; i < vc->values_num; i++)
518                 {
519                         fprintf (fh, " %s=", vc->ds->ds[i].name);
520                         if (isnan (vc->gauge[i]))
521                                 fprintf (fh, "NaN");
522                         else
523                                 fprintf (fh, "%12e", vc->gauge[i]);
524                 }
525         }
527         /* Free the mutex as soon as possible and definitely before flushing */
528         pthread_mutex_unlock (&cache_lock);
530         fprintf (fh, "\n");
531         fflush (fh);
533         return (0);
534 } /* int us_handle_getval */
536 static int us_handle_putval (FILE *fh, char **fields, int fields_num)
538         char *hostname;
539         char *plugin;
540         char *plugin_instance;
541         char *type;
542         char *type_instance;
543         int   status;
544         int   i;
546         const data_set_t *ds;
547         value_list_t vl = VALUE_LIST_INIT;
549         char **value_ptr;
551         if (fields_num != 3)
552                 return (-1);
554         status = parse_identifier (fields[1], &hostname,
555                         &plugin, &plugin_instance,
556                         &type, &type_instance);
557         if (status != 0)
558                 return (-1);
560         if ((strlen (hostname) > sizeof (vl.host))
561                         || (strlen (plugin) > sizeof (vl.plugin))
562                         || (strlen (plugin_instance) > sizeof (vl.plugin_instance))
563                         || (strlen (type_instance) > sizeof (vl.type_instance)))
564                 return (-1);
566         strcpy (vl.host, hostname);
567         strcpy (vl.plugin, plugin);
568         strcpy (vl.plugin_instance, plugin_instance);
569         strcpy (vl.type_instance, type_instance);
571         { /* parse the time */
572                 char *t = fields[2];
573                 char *v = strchr (t, ':');
574                 if (v == NULL)
575                         return (-1);
576                 *v = '\0'; v++;
578                 vl.time = (time_t) atoi (t);
579                 if (vl.time == 0)
580                         vl.time = time (NULL);
582                 fields[2] = v;
583         }
585         ds = plugin_get_ds (type);
586         if (ds == NULL)
587                 return (-1);
589         value_ptr = (char **) calloc (ds->ds_num, sizeof (char *));
590         if (value_ptr == NULL)
591                 return (-1);
594         { /* parse the value-list. It's colon-separated. */
595                 char *dummy;
596                 char *ptr;
597                 char *saveptr;
599                 i = 0;
600                 dummy = fields[2];
601                 saveptr = NULL;
602                 while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
603                 {
604                         dummy = NULL;
605                         if (i >= ds->ds_num)
606                         {
607                                 i = ds->ds_num + 1;
608                                 break;
609                         }
610                         value_ptr[i] = ptr;
611                         i++;
612                 }
614                 if (i != ds->ds_num)
615                 {
616                         free (value_ptr);
617                         return (-1);
618                 }
619         } /* done parsing the value-list */
621         vl.values_len = fields_num - 2;
622         vl.values = (value_t *) malloc (vl.values_len * sizeof (value_t));
623         if (vl.values == NULL)
624         {
625                 free (value_ptr);
626                 return (-1);
627         }
628         vl.values_len = ds->ds_num;
630         for (i = 0; i < ds->ds_num; i++)
631         {
632                 if (strcmp (value_ptr[i], "U") == 0)
633                         vl.values[i].gauge = NAN;
634                 else if (ds->ds[i].type == DS_TYPE_COUNTER)
635                         vl.values[i].counter = atoll (value_ptr[i]);
636                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
637                         vl.values[i].gauge = atof (value_ptr[i]);
638         } /* for (i = 2 .. fields_num) */
639         sfree (value_ptr);
641         plugin_dispatch_values (type, &vl);
643         sfree (vl.values); 
645         return (0);
646 } /* int us_handle_putval */
648 static void *us_handle_client (void *arg)
650         int fd;
651         FILE *fh;
652         char buffer[1024];
653         char *fields[128];
654         int   fields_num;
656         fd = *((int *) arg);
657         free (arg);
658         arg = NULL;
660         DEBUG ("Reading from fd #%i", fd);
662         fh = fdopen (fd, "r+");
663         if (fh == NULL)
664         {
665                 char errbuf[1024];
666                 ERROR ("unixsock plugin: fdopen failed: %s",
667                                 sstrerror (errno, errbuf, sizeof (errbuf)));
668                 close (fd);
669                 pthread_exit ((void *) 1);
670         }
672         while (fgets (buffer, sizeof (buffer), fh) != NULL)
673         {
674                 int len;
676                 len = strlen (buffer);
677                 while ((len > 0)
678                                 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
679                         buffer[--len] = '\0';
681                 if (len == 0)
682                         continue;
684                 DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
686                 fields_num = strsplit (buffer, fields,
687                                 sizeof (fields) / sizeof (fields[0]));
689                 if (fields_num < 1)
690                 {
691                         close (fd);
692                         break;
693                 }
695                 if (strcasecmp (fields[0], "getval") == 0)
696                 {
697                         us_handle_getval (fh, fields, fields_num);
698                 }
699                 else if (strcasecmp (fields[0], "putval") == 0)
700                 {
701                         us_handle_putval (fh, fields, fields_num);
702                 }
703                 else
704                 {
705                         fprintf (fh, "Unknown command: %s\n", fields[0]);
706                         fflush (fh);
707                 }
708         } /* while (fgets) */
710         DEBUG ("Exiting..");
711         close (fd);
713         pthread_exit ((void *) 0);
714 } /* void *us_handle_client */
716 static void *us_server_thread (void *arg)
718         int  status;
719         int *remote_fd;
720         pthread_t th;
721         pthread_attr_t th_attr;
723         if (us_open_socket () != 0)
724                 pthread_exit ((void *) 1);
726         while (42)
727         {
728                 DEBUG ("Calling accept..");
729                 status = accept (sock_fd, NULL, NULL);
730                 if (status < 0)
731                 {
732                         char errbuf[1024];
734                         if (errno == EINTR)
735                                 continue;
737                         ERROR ("unixsock plugin: accept failed: %s",
738                                         sstrerror (errno, errbuf, sizeof (errbuf)));
739                         close (sock_fd);
740                         sock_fd = -1;
741                         pthread_exit ((void *) 1);
742                 }
744                 remote_fd = (int *) malloc (sizeof (int));
745                 if (remote_fd == NULL)
746                 {
747                         char errbuf[1024];
748                         WARNING ("unixsock plugin: malloc failed: %s",
749                                         sstrerror (errno, errbuf, sizeof (errbuf)));
750                         close (status);
751                         continue;
752                 }
753                 *remote_fd = status;
755                 DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
757                 pthread_attr_init (&th_attr);
758                 pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
760                 status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
761                 if (status != 0)
762                 {
763                         char errbuf[1024];
764                         WARNING ("unixsock plugin: pthread_create failed: %s",
765                                         sstrerror (errno, errbuf, sizeof (errbuf)));
766                         close (*remote_fd);
767                         free (remote_fd);
768                         continue;
769                 }
770         } /* while (42) */
772         return ((void *) 0);
773 } /* void *us_server_thread */
775 static int us_config (const char *key, const char *val)
777         if (strcasecmp (key, "SocketFile") == 0)
778         {
779                 sfree (sock_file);
780                 sock_file = strdup (val);
781         }
782         else if (strcasecmp (key, "SocketGroup") == 0)
783         {
784                 sfree (sock_group);
785                 sock_group = strdup (val);
786         }
787         else if (strcasecmp (key, "SocketPerms") == 0)
788         {
789                 sock_perms = (int) strtol (val, NULL, 8);
790         }
791         else
792         {
793                 return (-1);
794         }
796         return (0);
797 } /* int us_config */
799 static int us_init (void)
801         int status;
803         status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
804         if (status != 0)
805         {
806                 char errbuf[1024];
807                 ERROR ("unixsock plugin: pthread_create failed: %s",
808                                 sstrerror (errno, errbuf, sizeof (errbuf)));
809                 return (-1);
810         }
812         return (0);
813 } /* int us_init */
815 static int us_shutdown (void)
817         void *ret;
819         if (listen_thread != (pthread_t) 0)
820         {
821                 pthread_kill (listen_thread, SIGTERM);
822                 pthread_join (listen_thread, &ret);
823                 listen_thread = (pthread_t) 0;
824         }
826         plugin_unregister_init ("unixsock");
827         plugin_unregister_write ("unixsock");
828         plugin_unregister_shutdown ("unixsock");
830         return (0);
831 } /* int us_shutdown */
833 static int us_write (const data_set_t *ds, const value_list_t *vl)
835         cache_update (ds, vl);
836         cache_flush (2 * atoi (COLLECTD_STEP));
838         return (0);
841 void module_register (void)
843         plugin_register_config ("unixsock", us_config,
844                         config_keys, config_keys_num);
845         plugin_register_init ("unixsock", us_init);
846         plugin_register_write ("unixsock", us_write);
847         plugin_register_shutdown ("unixsock", us_shutdown);
848 } /* void module_register (void) */
850 /* vim: set sw=4 ts=4 sts=4 tw=78 : */