3da9e0f492c53c68c22882b2e654c441d848a37a
[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 /* Returns non-zero only if the request could not be handled gracefully. */
508 static int handle_register_read (ce_connection_info_t *cinfo, /* {{{ */
509                 const ErlMessage *req)
510 {
511         ETERM *eterm_cb;
512         ETERM *rpc_args;
513         ETERM *rpc_reply;
514
515         if ((cinfo == NULL) || (req == NULL))
516                 return (EINVAL);
517
518         eterm_cb = erl_element (2, req->msg);
519
520         if (ERL_TYPE (eterm_cb) != ERL_FUNCTION)
521         {
522                 erl_free_term (eterm_cb);
523                 send_error (cinfo->fd, req->from,
524                                 "Argument to `register_read' must be a callback function.");
525                 return (0);
526         }
527
528         send_atom (cinfo->fd, req->from, "success");
529
530         /* FIXME: The following demonstrates how to call this function. This should
531          * be moved somewhere else, of course. */
532
533         /* --- 8< --- */
534         rpc_args = erl_format ("[~w,[]]", eterm_cb);
535         if (rpc_args == NULL)
536         {
537                 erl_free_term (eterm_cb);
538                 send_error (cinfo->fd, req->from,
539                                 "erl_format failed. Sorry.");
540                 return (0);
541         }
542
543         rpc_reply = erl_rpc (cinfo->fd,
544                         /* module = */ "erlang", /* function = */ "apply",
545                         /* arguments = */ rpc_args);
546
547         erl_free_compound (rpc_args);
548         /* Right now, the return value is not used. */
549         erl_free_compound (rpc_reply);
550         /* --- >8 --- */
551
552         erl_free_term (eterm_cb); /* XXX: This must stay here. */
553         return (0);
554 } /* }}} int handle_dispatch_values */
555
556 static void *handle_client_thread (void *arg) /* {{{ */
557 {
558         ce_connection_info_t *cinfo;
559         ErlMessage emsg;
560         unsigned char buffer[4096];
561
562         cinfo = arg;
563
564         DEBUG ("erlang plugin: handle_client_thread[%i]: Handling client %s.",
565                         cinfo->fd, cinfo->conn.nodename);
566
567         emsg.from = NULL;
568         emsg.to = NULL;
569         emsg.msg = NULL;
570
571         while (42)
572         {
573                 int status;
574
575                 erl_free_term (emsg.from);
576                 emsg.from = NULL;
577                 erl_free_term (emsg.to);
578                 emsg.to = NULL;
579                 erl_free_term (emsg.msg);
580                 emsg.msg = NULL;
581
582                 status = erl_receive_msg (cinfo->fd, buffer, sizeof (buffer), &emsg);
583                 if (status == ERL_TICK)
584                         continue;
585
586                 if (status == ERL_ERROR)
587                         break;
588
589                 if (emsg.type == ERL_REG_SEND)
590                 {
591                         ETERM *func;
592                         ETERM *reply;
593
594                         if (!ERL_IS_TUPLE (emsg.msg))
595                         {
596                                 ERROR ("erlang plugin: Message is not a tuple.");
597                                 send_atom (cinfo->fd, emsg.from, "error");
598                                 continue;
599                         }
600
601                         func = erl_element (1, emsg.msg);
602                         if (!ERL_IS_ATOM (func))
603                         {
604                                 ERROR ("erlang plugin: First element is not an atom!");
605                                 send_atom (cinfo->fd, emsg.from, "error");
606                                 erl_free_term (func);
607                                 continue;
608                         }
609
610                         DEBUG ("erlang plugin: Wanted function is: %s.", ERL_ATOM_PTR (func));
611                         reply = NULL;
612                         if (strcmp ("dispatch_values", ERL_ATOM_PTR (func)) == 0)
613                                 status = handle_dispatch_values (cinfo, &emsg);
614                         else if (strcmp ("register_read", ERL_ATOM_PTR (func)) == 0)
615                                 status = handle_register_read (cinfo, &emsg);
616                         else
617                         {
618                                 ERROR ("erlang plugin: Received request for invalid function `%s'.",
619                                                 ERL_ATOM_PTR (func));
620                                 send_atom (cinfo->fd, emsg.from, "error");
621                                 status = 0;
622                         }
623
624                         /* Check for fatal errors in the callback functions. */
625                         if (status != 0)
626                         {
627                                 ERROR ("erlang plugin: Handling request for `%s' failed.",
628                                                 ERL_ATOM_PTR (func));
629                                 erl_free_term (func);
630                                 break;
631                         }
632
633                         erl_free_term (func);
634                 }
635                 else if (emsg.type == ERL_EXIT)
636                 {
637                         DEBUG ("erlang plugin: handle_client_thread[%i]: "
638                                         "Received exit message.", cinfo->fd);
639                         break;
640                 }
641                 else
642                 {
643                         ERROR ("erlang plugin: Message type not handled: %i.", emsg.type);
644                 }
645         } /* while (42) */
646
647         erl_free_term (emsg.from);
648         emsg.from = NULL;
649         erl_free_term (emsg.to);
650         emsg.to = NULL;
651         erl_free_term (emsg.msg);
652         emsg.msg = NULL;
653
654         DEBUG ("erlang plugin: handle_client_thread[%i]: Exiting.", cinfo->fd);
655
656         close (cinfo->fd);
657         free (cinfo);
658
659         pthread_exit ((void *) 0);
660         return ((void *) 0);
661 } /* }}} void *handle_client_thread */
662
663 static int create_listen_socket (void) /* {{{ */
664 {
665         struct addrinfo ai_hints;
666         struct addrinfo *ai_list;
667         struct addrinfo *ai_ptr;
668         int sock_descr;
669         int status;
670         int numeric_serv;
671
672         sock_descr = -1;
673
674         memset (&ai_hints, 0, sizeof (ai_hints));
675         /* AI_PASSIVE => returns INADDR_ANY */
676         ai_hints.ai_flags = AI_PASSIVE;
677 #ifdef AI_ADDRCONFIG
678         ai_hints.ai_flags |= AI_ADDRCONFIG;
679 #endif
680         /* IPv4 only :( */
681         ai_hints.ai_family = AF_INET;
682         ai_hints.ai_socktype = SOCK_STREAM;
683
684         ai_list = NULL;
685         status = getaddrinfo (/* node = */ NULL, /* service = */ conf_service,
686                         &ai_hints, &ai_list);
687         if (status != 0)
688         {
689                 ERROR ("erlang plugin: getaddrinfo failed: %s", gai_strerror (status));
690                 return (-1);
691         }
692
693         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
694         {
695                 struct sockaddr_in *sa_in;
696                 struct in_addr *sin_addr;
697                 int yes;
698
699                 assert (ai_ptr->ai_family == AF_INET);
700                 sa_in = (struct sockaddr_in *) ai_ptr->ai_addr;
701                 sin_addr = &sa_in->sin_addr;
702                 numeric_serv = (int) ntohs (sa_in->sin_port);
703
704                 /* Dunno if calling this multiple times is legal. Since it wants to have
705                  * the sin_addr for some reason this is the best place to call this,
706                  * though. -octo */
707                 status = erl_connect_xinit (/* host name = */ conf_hostname,
708                                 /* plain node name = */ conf_nodename,
709                                 /* full node name  = */ conf_fullname,
710                                 /* our address     = */ sin_addr,
711                                 /* secret cookie   = */ conf_cookie,
712                                 /* instance number = */ 0);
713                 if (status < 0)
714                 {
715                         ERROR ("erlang plugin: erl_connect_xinit failed with status %i.",
716                                         status);
717                         continue;
718                 }
719
720                 sock_descr = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
721                                 ai_ptr->ai_protocol);
722                 if (sock_descr < 0)
723                 {
724                         ERROR ("erlang plugin: socket(2) failed.");
725                         continue;
726                 }
727
728                 yes = 1;
729                 status = setsockopt (sock_descr, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
730                 if (status != 0)
731                 {
732                         ERROR ("erlang plugin: setsockopt(2) failed.");
733                         close (sock_descr);
734                         sock_descr = -1;
735                         continue;
736                 }
737
738                 status = bind (sock_descr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
739                 if (status != 0)
740                 {
741                         ERROR ("erlang plugin: bind(2) failed.");
742                         close (sock_descr);
743                         sock_descr = -1;
744                         continue;
745                 }
746
747                 status = listen (sock_descr, /* backlog = */ 10);
748                 if (status != 0)
749                 {
750                         ERROR ("erlang plugin: listen(2) failed.");
751                         close (sock_descr);
752                         sock_descr = -1;
753                         continue;
754                 }
755
756                 break;
757         } /* for (ai_list) */
758
759         freeaddrinfo (ai_list);
760
761         if (sock_descr >= 0)
762         {
763                 status = erl_publish (numeric_serv);
764                 if (status < 0)
765                 {
766                         ERROR ("erlang plugin: erl_publish (%i) failed with status %i. "
767                                         "Is epmd running?", numeric_serv, status);
768                         close (sock_descr);
769                         sock_descr = -1;
770                         return (-1);
771                 }
772         }
773
774         if (sock_descr >= 0)
775         {
776                 INFO ("erlang plugin: Created Erlang socket: Nodename %s, Port %i, "
777                                 "Cookie %s.",
778                                 conf_fullname, numeric_serv, conf_cookie);
779         }
780
781         return (sock_descr);
782 } /* }}} int create_listen_socket */
783
784 void *listen_thread (void *arg) /* {{{ */
785 {
786         int listen;
787         int fd;
788
789         ErlConnect conn;
790
791         /* I have no fucking idea what this does, nor what the arguments are. Didn't
792          * find any comprehensive docs yet. */
793         erl_init (/* void *x = */ NULL, /* long y = */ 0);
794
795         listen = create_listen_socket ();
796         if (listen < 0)
797                 exit (EXIT_FAILURE);
798
799         while (42)
800         {
801                 pthread_t tid;
802                 pthread_attr_t tattr;
803                 ce_connection_info_t *arg;
804
805                 fd = erl_accept (listen, &conn);
806                 if (fd < 0)
807                 {
808                         ERROR ("erlang plugin: erl_accept failed with status %i.", fd);
809                         close (listen);
810                         exit (EXIT_FAILURE);
811                 }
812                 DEBUG ("erlang plugin: Got connection from %s on fd %i.",
813                                 conn.nodename, fd);
814
815                 pthread_attr_init (&tattr);
816                 pthread_attr_setdetachstate (&tattr, PTHREAD_CREATE_DETACHED);
817
818                 arg = malloc (sizeof (*arg));
819                 if (arg == NULL)
820                 {
821                         ERROR ("erlang plugin: malloc failed.");
822                         close (fd);
823                         continue;
824                 }
825                 memset (arg, 0, sizeof (*arg));
826
827                 arg->fd = fd;
828                 memcpy (&arg->conn, &conn, sizeof (conn));
829
830                 pthread_create (&tid, &tattr, handle_client_thread, arg);
831         } /* while (42) */
832
833         pthread_exit ((void *) 0);
834         return ((void *) 0);
835 } /* }}} void *listen_thread */
836
837 static int ce_init (void) /* {{{ */
838 {
839         if (!listen_thread_running)
840         {
841                 int status;
842
843                 status = pthread_create (&listen_thread_id,
844                                 /* attr = */ NULL,
845                                 listen_thread,
846                                 /* args = */ NULL);
847                 if (status == 0)
848                         listen_thread_running = true;
849         }
850
851         return (0);
852 } /* }}} int ce_init */
853
854 static int ce_config (const char *key, const char *value) /* {{{ */
855 {
856         if (strcasecmp ("BindTo", key) == 0)
857         {
858                 sstrncpy (conf_node, value, sizeof (conf_node));
859         }
860         else if (strcasecmp ("BindPort", key) == 0)
861         {
862                 sstrncpy (conf_service, value, sizeof (conf_service));
863         }
864         else if (strcasecmp ("Cookie", key) == 0)
865         {
866                 sstrncpy (conf_cookie, value, sizeof (conf_cookie));
867         }
868         else if (strcasecmp ("NodeName", key) == 0)
869         {
870                 const char *host;
871
872                 host = strchr (value, '@');
873                 if (host == NULL)
874                 {
875                         sstrncpy (conf_nodename, value, sizeof (conf_nodename));
876                         sstrncpy (conf_hostname, hostname_g, sizeof (conf_hostname));
877                         ssnprintf (conf_fullname, sizeof (conf_fullname), "%s@%s",
878                                         conf_nodename, conf_hostname);
879                 }
880                 else /* if (host != NULL) */
881                 {
882                         char *tmp;
883
884                         sstrncpy (conf_nodename, value, sizeof (conf_nodename));
885                         sstrncpy (conf_hostname, host + 1, sizeof (conf_hostname));
886                         sstrncpy (conf_fullname, value, sizeof (conf_fullname));
887
888                         tmp = strchr (conf_nodename, '@');
889                         if (tmp != NULL)
890                                 *tmp = 0;
891                 }
892         }
893         else
894         {
895                 return (-1);
896         }
897
898         return (0);
899 } /* }}} int ce_config */
900
901 void module_register (void)
902 {
903         plugin_register_config ("erlang", ce_config, config_keys, config_keys_num);
904         plugin_register_init ("erlang", ce_init);
905 }
906
907 /* vim: set sw=2 ts=2 noet fdm=marker : */