erlang plugin: Use #ifdefs around "long longs".
[collectd.git] / src / erlang.c
1 /**
2  * collectd - src/erlang.c
3  * Copyright (C) 2009  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 #include "collectd.h"
23 #include "plugin.h"
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29
30 #include <pthread.h>
31
32 #include <erl_interface.h>
33 #include <ei.h>
34
35 /* 
36  * Private data structures
37  */
38 struct ce_connection_info_s
39 {
40         int fd;
41         ErlConnect conn;
42 };
43 typedef struct ce_connection_info_s ce_connection_info_t;
44
45 /*
46  * Private variables
47  */
48 static pthread_t listen_thread_id;
49 static _Bool     listen_thread_running = false;
50
51 static char conf_service[NI_MAXSERV] = "29157";
52 static char conf_cookie[256] = "ceisaequ";
53
54 /*
55  * Private functions
56  */
57 static int send_atom (int fd, ETERM *to, const char *atom) /* {{{ */
58 {
59         ETERM *reply;
60         int status;
61
62         reply = erl_mk_atom (atom);
63         if (reply == NULL)
64                 return (ENOMEM);
65
66         status = erl_send (fd, to, reply);
67         erl_free_term (reply);
68
69         if (status == 1)
70                 return (0);
71         else
72                 return (erl_errno);
73 } /* }}} int send_atom */
74
75 static int send_error (int fd, ETERM *to, const char *message) /* {{{ */
76 {
77         ETERM *reply;
78         int status;
79
80         DEBUG ("erlang plugin: send_error: message = %s.", message);
81         reply = erl_format ("{~a,~s}", "error", message);
82
83         status = erl_send (fd, to, reply);
84         if (status != 1)
85                 status = erl_errno;
86         else
87                 status = 0;
88
89         erl_free_term (reply);
90
91         return (status);
92 } /* }}} int send_error */
93
94 static int eterm_to_int (const ETERM *term, int *ret_int) /* {{{ */
95 {
96         if ((term == NULL) || (ret_int == NULL))
97                 return (EINVAL);
98
99         switch (ERL_TYPE (term))
100         {
101                 case ERL_INTEGER:
102                         *ret_int = (int) ERL_INT_VALUE (term);
103                         break;
104
105                 case ERL_U_INTEGER:
106                         *ret_int = (int) ERL_INT_UVALUE (term);
107                         break;
108
109                 case ERL_FLOAT:
110                         *ret_int = (int) (ERL_FLOAT_VALUE (term) + .5);
111                         break;
112
113 #ifdef ERL_LONGLONG
114                 case ERL_LONGLONG:
115                         *ret_int = (int) ERL_LL_VALUE (term);
116                         break;
117 #endif /* ERL_LONGLONG */
118
119 #ifdef ERL_U_LONGLONG
120                 case ERL_U_LONGLONG:
121                         *ret_int = (int) ERL_LL_UVALUE (term);
122                         break;
123 #endif /* ERL_U_LONGLONG */
124
125                 default:
126                         ERROR ("erlang plugin: Don't know how to cast "
127                                         "erlang type %#x to int.", (unsigned int) ERL_TYPE (term));
128                         return (ENOTSUP);
129         } /* switch (ERL_TYPE (term)) */
130
131         return (0);
132 } /* }}} int eterm_to_int */
133
134 static int eterm_to_time_t (const ETERM *term, time_t *ret_time) /* {{{ */
135 {
136         if ((term == NULL) || (ret_time == NULL))
137                 return (EINVAL);
138
139         if (ERL_IS_NIL (term)
140                         || (ERL_IS_ATOM (term)
141                                 && ((strcmp ("now", ERL_ATOM_PTR (term)) == 0)
142                                         || (strcmp ("undefined", ERL_ATOM_PTR (term)) == 0))))
143         {
144                 *ret_time = time (NULL);
145                 return (0);
146         }
147
148         switch (ERL_TYPE (term))
149         {
150                 case ERL_INTEGER:
151                         *ret_time = (time_t) ERL_INT_VALUE (term);
152                         break;
153
154                 case ERL_U_INTEGER:
155                         *ret_time = (time_t) ERL_INT_UVALUE (term);
156                         break;
157
158                 case ERL_ATOM:
159                         if ((strcmp ("now", ERL_ATOM_PTR (term)) == 0)
160                                         || (strcmp ("undefined", ERL_ATOM_PTR (term)) == 0))
161                         {
162                                 *ret_time = time (NULL);
163                         }
164                         else
165                         {
166                                 ERROR ("erlang plugin: Invalid atom for time: %s.",
167                                                 ERL_ATOM_PTR (term));
168                                 return (ENOTSUP);
169                         }
170                         break;
171
172                 case ERL_FLOAT:
173                         *ret_time = (time_t) (ERL_FLOAT_VALUE (term) + .5);
174                         break;
175
176 #ifdef ERL_LONGLONG
177                 case ERL_LONGLONG:
178                         *ret_time = (time_t) ERL_LL_VALUE (term);
179                         break;
180 #endif /* ERL_LONGLONG */
181
182 #ifdef ERL_U_LONGLONG
183                 case ERL_U_LONGLONG:
184                         *ret_time = (time_t) ERL_LL_UVALUE (term);
185                         break;
186 #endif /* ERL_U_LONGLONG */
187
188                 default:
189                         ERROR ("erlang plugin: Don't know how to cast "
190                                         "erlang type %#x to time_t.", (unsigned int) ERL_TYPE (term));
191                         return (ENOTSUP);
192         } /* switch (ERL_TYPE (term)) */
193
194         return (0);
195 } /* }}} int eterm_to_time_t */
196
197 static int eterm_to_string (const ETERM *term, char *buffer, size_t buffer_size) /* {{{ */
198 {
199         char *tmp;
200
201         if ((term == NULL) || (buffer == NULL) || (buffer_size <= 0))
202                 return (EINVAL);
203
204         memset (buffer, 0, buffer_size);
205
206         if (ERL_IS_EMPTY_LIST (term)
207                         || ERL_IS_NIL (term)
208                         || (ERL_IS_ATOM (term)
209                                 && (strcmp ("undefined", ERL_ATOM_PTR (term)) == 0)))
210         {
211                 buffer[0] = 0;
212                 return (0);
213         }
214
215         if (!ERL_IS_LIST (term))
216                 return (-1);
217
218         tmp = erl_iolist_to_string (term);
219         if (tmp == NULL)
220                 return (-1);
221
222         strncpy (buffer, tmp, buffer_size - 1);
223         erl_free (tmp);
224
225         return (0);
226 } /* }}} int eterm_to_string */
227
228 static int eterm_to_value (const ETERM *term, int ds_type, /* {{{ */
229                 value_t *value)
230 {
231         if ((term == NULL) || (value == NULL))
232                 return (EINVAL);
233
234         switch (ERL_TYPE (term))
235         {
236                 case ERL_INTEGER:
237                 {
238                         int v = ERL_INT_VALUE (term);
239                         switch (ds_type)
240                         {
241                                 case DS_TYPE_COUNTER:  value->counter  = (counter_t)  v; break;
242                                 case DS_TYPE_GAUGE:    value->gauge    = (gauge_t)    v; break;
243                                 case DS_TYPE_DERIVE:   value->derive   = (derive_t)   v; break;
244                                 case DS_TYPE_ABSOLUTE: value->absolute = (absolute_t) v; break;
245                         }
246                         break;
247                 }
248
249                 case ERL_U_INTEGER:
250                 {
251                         unsigned int v = ERL_INT_UVALUE (term);
252                         switch (ds_type)
253                         {
254                                 case DS_TYPE_COUNTER:  value->counter  = (counter_t)  v; break;
255                                 case DS_TYPE_GAUGE:    value->gauge    = (gauge_t)    v; break;
256                                 case DS_TYPE_DERIVE:   value->derive   = (derive_t)   v; break;
257                                 case DS_TYPE_ABSOLUTE: value->absolute = (absolute_t) v; break;
258                         }
259                         break;
260                 }
261
262                 case ERL_FLOAT:
263                 {
264                         double v = ERL_FLOAT_VALUE (term);
265                         switch (ds_type)
266                         {
267                                 case DS_TYPE_COUNTER:  value->counter  = (counter_t)  v; break;
268                                 case DS_TYPE_GAUGE:    value->gauge    = (gauge_t)    v; break;
269                                 case DS_TYPE_DERIVE:   value->derive   = (derive_t)   v; break;
270                                 case DS_TYPE_ABSOLUTE: value->absolute = (absolute_t) v; break;
271                         }
272                         break;
273                 }
274
275 #ifdef ERL_LONGLONG
276                 case ERL_LONGLONG:
277                 {
278                         long long v = ERL_LL_VALUE (term);
279                         switch (ds_type)
280                         {
281                                 case DS_TYPE_COUNTER:  value->counter  = (counter_t)  v; break;
282                                 case DS_TYPE_GAUGE:    value->gauge    = (gauge_t)    v; break;
283                                 case DS_TYPE_DERIVE:   value->derive   = (derive_t)   v; break;
284                                 case DS_TYPE_ABSOLUTE: value->absolute = (absolute_t) v; break;
285                         }
286                         break;
287                 }
288 #endif /* ERL_LONGLONG */
289
290 #ifdef ERL_U_LONGLONG
291                 case ERL_U_LONGLONG:
292                 {
293                         unsigned long long v = ERL_LL_UVALUE (term);
294                         switch (ds_type)
295                         {
296                                 case DS_TYPE_COUNTER:  value->counter  = (counter_t)  v; break;
297                                 case DS_TYPE_GAUGE:    value->gauge    = (gauge_t)    v; break;
298                                 case DS_TYPE_DERIVE:   value->derive   = (derive_t)   v; break;
299                                 case DS_TYPE_ABSOLUTE: value->absolute = (absolute_t) v; break;
300                         }
301                         break;
302                 }
303 #endif /* ERL_U_LONGLONG */
304
305                 default:
306                         ERROR ("erlang plugin: Don't know how to cast "
307                                         "erlang type %#x to value_t.", (unsigned int) ERL_TYPE (term));
308                         return (ENOTSUP);
309         } /* switch (ERL_TYPE (term)) */
310
311         return (0);
312 } /* }}} int eterm_to_value */
313
314 static int eterm_to_values (const ETERM *term, const data_set_t *ds, /* {{{ */
315                 value_list_t *vl)
316 {
317         int ds_index;
318         int status;
319
320         if ((term == NULL) || (ds == NULL) || (vl == NULL))
321                 return (EINVAL);
322
323         if (!ERL_IS_LIST (term))
324                 return (-1);
325
326         free (vl->values);
327         vl->values = NULL;
328         vl->values_len = 0;
329
330         while (!ERL_IS_EMPTY_LIST (term))
331         {
332                 const ETERM *eterm_value;
333                 value_t *tmp;
334
335                 if (ds_index >= ds->ds_num)
336                 {
337                         ds_index = ds->ds_num + 1;
338                         status = 0;
339                         break;
340                 }
341
342                 tmp = realloc (vl->values, sizeof (*tmp) * (vl->values_len + 1));
343                 if (tmp == NULL)
344                 {
345                         status = ENOMEM;
346                         break;
347                 }
348                 vl->values = tmp;
349
350                 eterm_value = ERL_CONS_HEAD (term);
351                 term = ERL_CONS_TAIL (term);
352
353                 status = eterm_to_value (eterm_value, ds->ds[ds_index].type,
354                                 vl->values + vl->values_len);
355                 if (status != 0)
356                         break;
357
358                 vl->values_len++;
359                 ds_index++;
360         }
361
362         if ((status == 0) && (ds_index != ds->ds_num))
363                 NOTICE ("erlang plugin: Incorrect number of values received for type %s: "
364                                 "Expected %i, got %i.", ds->type, ds->ds_num, ds_index);
365
366         if ((status != 0) || (ds_index != ds->ds_num))
367         {
368                 free (vl->values);
369                 vl->values = NULL;
370                 vl->values_len = 0;
371                 return (status);
372         }
373
374         return (0);
375 } /* }}} int eterm_to_values */
376
377 static int eterm_to_value_list (const ETERM *term, value_list_t *vl) /* {{{ */
378 {
379         ETERM *tmp;
380         int status;
381         const data_set_t *ds;
382
383         if ((term == NULL) || (vl == NULL))
384                 return (EINVAL);
385
386         if (!ERL_IS_TUPLE (term) || (ERL_TUPLE_SIZE (term) != 9))
387                 return (EINVAL);
388
389         tmp = erl_element (1, term);
390         if (!ERL_IS_ATOM (tmp)
391                         || (strcmp ("value_list", ERL_ATOM_PTR (tmp)) != 0))
392         {
393                 erl_free_term (tmp);
394                 return (-1);
395         }
396         erl_free_term (tmp);
397
398         status = 0;
399         do
400         {
401 #define TUPLE_ELEM_TO_CHAR_ARRAY(idx,buf) \
402                 tmp = erl_element ((idx), term); \
403                 status = eterm_to_string (tmp, (buf), sizeof (buf)); \
404                 erl_free_term (tmp); \
405                 if (status != 0) \
406                         break;
407
408                 TUPLE_ELEM_TO_CHAR_ARRAY (2, vl->host);
409                 TUPLE_ELEM_TO_CHAR_ARRAY (3, vl->plugin);
410                 TUPLE_ELEM_TO_CHAR_ARRAY (4, vl->plugin_instance);
411                 TUPLE_ELEM_TO_CHAR_ARRAY (5, vl->type);
412                 TUPLE_ELEM_TO_CHAR_ARRAY (6, vl->type_instance);
413
414                 ds = plugin_get_ds (vl->type);
415                 if (ds == NULL)
416                 {
417                         status = -1;
418                         break;
419                 }
420
421                 tmp = erl_element (7, term);
422                 status = eterm_to_time_t (tmp, &vl->time);
423                 erl_free_term (tmp);
424                 if (status != 0)
425                         break;
426
427                 tmp = erl_element (8, term);
428                 status = eterm_to_int (tmp, &vl->interval);
429                 erl_free_term (tmp);
430                 if (status != 0)
431                         break;
432                 if (vl->interval < 1)
433                         vl->interval = interval_g;
434
435                 tmp = erl_element (9, term);
436                 status = eterm_to_values (tmp, ds, vl);
437                 erl_free_term (tmp);
438                 if (status != 0)
439                         break;
440
441 #undef TUPLE_ELEM_TO_CHAR_ARRAY
442         } while (0);
443
444         if (status != 0)
445                 return (status);
446
447         /* validate the struct */
448         if ((vl->host[0] == 0) || (vl->plugin[0] == 0) || (vl->type[0] == 0))
449                 return (-1);
450
451         if (ds->ds_num != vl->values_len)
452                 return (-1);
453
454         return (0);
455 } /* }}} int eterm_to_value_list */
456
457 /* Returns non-zero only if the request could not be handled gracefully. */
458 static int handle_dispatch_values (ce_connection_info_t *cinfo, /* {{{ */
459                 const ErlMessage *req)
460 {
461         ETERM *eterm_vl;
462         value_list_t vl;
463         int status;
464
465         memset (&vl, 0, sizeof (vl));
466         vl.values = NULL;
467
468         eterm_vl = erl_element (2, req->msg);
469         status = eterm_to_value_list (eterm_vl, &vl);
470         erl_free_term (eterm_vl);
471
472         if (status != 0)
473         {
474                 free (vl.values);
475                 send_error (cinfo->fd, req->from, "Cannot parse argument as value list.");
476                 return (0);
477         }
478
479         status = plugin_dispatch_values (&vl);
480         if (status != 0)
481         {
482                 free (vl.values);
483                 send_error (cinfo->fd, req->from, "plugin_dispatch_values failed.");
484                 return (0);
485         }
486
487         free (vl.values);
488         send_atom (cinfo->fd, req->from, "success");
489
490         return (0);
491 } /* }}} int handle_dispatch_values */
492
493 static void *handle_client_thread (void *arg) /* {{{ */
494 {
495         ce_connection_info_t *cinfo;
496         ErlMessage emsg;
497         unsigned char buffer[4096];
498
499         cinfo = arg;
500
501         DEBUG ("erlang plugin: handle_client_thread[%i]: Handling client %s.",
502                         cinfo->fd, cinfo->conn.nodename);
503
504         emsg.from = NULL;
505         emsg.to = NULL;
506         emsg.msg = NULL;
507
508         while (42)
509         {
510                 int status;
511
512                 erl_free_term (emsg.from);
513                 emsg.from = NULL;
514                 erl_free_term (emsg.to);
515                 emsg.to = NULL;
516                 erl_free_term (emsg.msg);
517                 emsg.msg = NULL;
518
519                 status = erl_receive_msg (cinfo->fd, buffer, sizeof (buffer), &emsg);
520                 if (status == ERL_TICK)
521                         continue;
522
523                 if (status == ERL_ERROR)
524                         break;
525
526                 if (emsg.type == ERL_REG_SEND)
527                 {
528                         ETERM *func;
529                         ETERM *reply;
530
531                         if (!ERL_IS_TUPLE (emsg.msg))
532                         {
533                                 ERROR ("erlang plugin: Message is not a tuple.");
534                                 send_atom (cinfo->fd, emsg.from, "error");
535                                 continue;
536                         }
537
538                         func = erl_element (1, emsg.msg);
539                         if (!ERL_IS_ATOM (func))
540                         {
541                                 ERROR ("erlang plugin: First element is not an atom!");
542                                 send_atom (cinfo->fd, emsg.from, "error");
543                                 erl_free_term (func);
544                                 continue;
545                         }
546
547                         DEBUG ("erlang plugin: Wanted function is: %s.", ERL_ATOM_PTR (func));
548                         reply = NULL;
549                         if (strcmp ("dispatch_values", ERL_ATOM_PTR (func)) == 0)
550                                 status = handle_dispatch_values (cinfo, &emsg);
551                         else
552                         {
553                                 ERROR ("erlang plugin: Received request for invalid function `%s'.",
554                                                 ERL_ATOM_PTR (func));
555                                 send_atom (cinfo->fd, emsg.from, "error");
556                                 status = 0;
557                         }
558
559                         /* Check for fatal errors in the callback functions. */
560                         if (status != 0)
561                         {
562                                 ERROR ("erlang plugin: Handling request for `%s' failed.",
563                                                 ERL_ATOM_PTR (func));
564                                 erl_free_term (func);
565                                 break;
566                         }
567
568                         erl_free_term (func);
569                 }
570                 else if (emsg.type == ERL_EXIT)
571                 {
572                         DEBUG ("erlang plugin: handle_client_thread[%i]: "
573                                         "Received exit message.", cinfo->fd);
574                         break;
575                 }
576                 else
577                 {
578                         ERROR ("erlang plugin: Message type not handled: %i.", emsg.type);
579                 }
580         } /* while (42) */
581
582         erl_free_term (emsg.from);
583         emsg.from = NULL;
584         erl_free_term (emsg.to);
585         emsg.to = NULL;
586         erl_free_term (emsg.msg);
587         emsg.msg = NULL;
588
589         DEBUG ("erlang plugin: handle_client_thread[%i]: Exiting.", cinfo->fd);
590
591         close (cinfo->fd);
592         free (cinfo);
593
594         pthread_exit ((void *) 0);
595         return ((void *) 0);
596 } /* }}} void *handle_client_thread */
597
598 static int create_listen_socket (void) /* {{{ */
599 {
600         struct addrinfo ai_hints;
601         struct addrinfo *ai_list;
602         struct addrinfo *ai_ptr;
603         int sock_descr;
604         int status;
605         int numeric_serv;
606
607         sock_descr = -1;
608
609         memset (&ai_hints, 0, sizeof (ai_hints));
610         /* AI_PASSIVE => returns INADDR_ANY */
611         ai_hints.ai_flags = AI_PASSIVE;
612 #ifdef AI_ADDRCONFIG
613         ai_hints.ai_flags |= AI_ADDRCONFIG;
614 #endif
615         /* IPv4 only :( */
616         ai_hints.ai_family = AF_INET;
617         ai_hints.ai_socktype = SOCK_STREAM;
618
619         ai_list = NULL;
620         status = getaddrinfo (/* node = */ NULL, /* service = */ conf_service,
621                         &ai_hints, &ai_list);
622         if (status != 0)
623         {
624                 ERROR ("erlang plugin: getaddrinfo failed: %s", gai_strerror (status));
625                 return (-1);
626         }
627
628         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
629         {
630                 struct sockaddr_in *sa_in;
631                 struct in_addr *sin_addr;
632                 int yes;
633
634                 assert (ai_ptr->ai_family == AF_INET);
635                 sa_in = (struct sockaddr_in *) ai_ptr->ai_addr;
636                 sin_addr = &sa_in->sin_addr;
637                 numeric_serv = (int) ntohs (sa_in->sin_port);
638
639                 /* Dunno if calling this multiple times is legal. Since it wants to have
640                  * the sin_addr for some reason this is the best place to call this,
641                  * though. -octo */
642                 status = erl_connect_xinit (/* host name = */ "leeloo",
643                                 /* plain node name = */ "collectd",
644                                 /* full node name  = */ "collectd@leeloo.lan.home.verplant.org",
645                                 /* our address     = */ sin_addr,
646                                 /* secret cookie   = */ conf_cookie,
647                                 /* instance number = */ 0);
648                 if (status < 0)
649                 {
650                         ERROR ("erlang plugin: erl_connect_xinit failed with status %i.",
651                                         status);
652                         continue;
653                 }
654
655                 sock_descr = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
656                                 ai_ptr->ai_protocol);
657                 if (sock_descr < 0)
658                 {
659                         ERROR ("erlang plugin: socket(2) failed.");
660                         continue;
661                 }
662
663                 yes = 1;
664                 status = setsockopt (sock_descr, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
665                 if (status != 0)
666                 {
667                         ERROR ("erlang plugin: setsockopt(2) failed.");
668                         close (sock_descr);
669                         sock_descr = -1;
670                         continue;
671                 }
672
673                 status = bind (sock_descr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
674                 if (status != 0)
675                 {
676                         ERROR ("erlang plugin: bind(2) failed.");
677                         close (sock_descr);
678                         sock_descr = -1;
679                         continue;
680                 }
681
682                 status = listen (sock_descr, /* backlog = */ 10);
683                 if (status != 0)
684                 {
685                         ERROR ("erlang plugin: listen(2) failed.");
686                         close (sock_descr);
687                         sock_descr = -1;
688                         continue;
689                 }
690
691                 break;
692         } /* for (ai_list) */
693
694         freeaddrinfo (ai_list);
695
696         if (sock_descr >= 0)
697         {
698                 status = erl_publish (numeric_serv);
699                 if (status < 0)
700                 {
701                         ERROR ("erlang plugin: erl_publish (%i) failed with status %i.", numeric_serv, status);
702                         close (sock_descr);
703                         sock_descr = -1;
704                         return (-1);
705                 }
706         }
707
708         return (sock_descr);
709 } /* }}} int create_listen_socket */
710
711 void *listen_thread (void *arg) /* {{{ */
712 {
713         int listen;
714         int fd;
715
716         ErlConnect conn;
717
718         /* I have no fucking idea what this does, nor what the arguments are. Didn't
719          * find any comprehensive docs yet. */
720         erl_init (/* void *x = */ NULL, /* long y = */ 0);
721
722         listen = create_listen_socket ();
723         if (listen < 0)
724                 exit (EXIT_FAILURE);
725
726         while (42)
727         {
728                 pthread_t tid;
729                 pthread_attr_t tattr;
730                 ce_connection_info_t *arg;
731
732                 fd = erl_accept (listen, &conn);
733                 if (fd < 0)
734                 {
735                         ERROR ("erlang plugin: erl_accept failed with status %i.", fd);
736                         close (listen);
737                         exit (EXIT_FAILURE);
738                 }
739                 DEBUG ("erlang plugin: Got connection from %s on fd %i.",
740                                 conn.nodename, fd);
741
742                 pthread_attr_init (&tattr);
743                 pthread_attr_setdetachstate (&tattr, PTHREAD_CREATE_DETACHED);
744
745                 arg = malloc (sizeof (*arg));
746                 if (arg == NULL)
747                 {
748                         ERROR ("erlang plugin: malloc failed.");
749                         close (fd);
750                         continue;
751                 }
752                 memset (arg, 0, sizeof (*arg));
753
754                 arg->fd = fd;
755                 memcpy (&arg->conn, &conn, sizeof (conn));
756
757                 pthread_create (&tid, &tattr, handle_client_thread, arg);
758         } /* while (42) */
759
760         pthread_exit ((void *) 0);
761         return ((void *) 0);
762 } /* }}} void *listen_thread */
763
764 static int ce_init (void) /* {{{ */
765 {
766         if (!listen_thread_running)
767         {
768                 int status;
769
770                 status = pthread_create (&listen_thread_id,
771                                 /* attr = */ NULL,
772                                 listen_thread,
773                                 /* args = */ NULL);
774                 if (status == 0)
775                         listen_thread_running = true;
776         }
777
778         return (0);
779 } /* }}} int ce_init */
780
781 void module_register (void)
782 {
783         plugin_register_init ("erlang", ce_init);
784 }
785
786 /* vim: set sw=2 ts=2 noet fdm=marker : */