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