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