1 /*
2  * guile-server.c - guile server modules
3  *
4  * Copyright (C) 2011-2013 Thien-Thi Nguyen
5  * Copyright (C) 2001, 2002, 2003, 2004 Stefan Jahn <stefan@lkcc.org>
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this package.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #if ENABLE_GUILE_SERVER
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <signal.h>
31 #if HAVE_FLOSS_H
32 # include <floss.h>
33 #endif
34 #include <libguile.h>
35 
36 #include "networking-headers.h"
37 #include "action.h"
38 #include "libserveez.h"
39 #include "misc-macros.h"
40 #include "gi.h"
41 #include "guile-api.h"
42 #include "guile.h"
43 #include "guile-bin.h"
44 #include "guile-server.h"
45 #include "unused.h"
46 
47 #define SVZ_PTR2SCM(x)  ((SCM) SVZ_PTR2NUM (x))
48 
49 typedef union {
50   SCM          sym;
51   char * const str;
52 } symstr_t;
53 
54 #include "gsk.c"                        /* (find-file "gsk-make") */
55 
56 static void
init_symbolset(size_t count,symstr_t set[count])57 init_symbolset (size_t count, symstr_t set[count])
58 {
59   size_t i;
60   symstr_t *ss;
61 
62   for (i = 0; i < count; i++)
63     {
64       ss = set + i;
65       ss->sym = gi_gc_protect (gi_symbol2scm (ss->str));
66     }
67 }
68 
69 #define INIT_SYMBOLSET(set)  init_symbolset (set ## _count, set)
70 
71 static SCM
protected_ht(size_t size)72 protected_ht (size_t size)
73 {
74   return gi_gc_protect (gi_make_hash_table (size));
75 }
76 
77 static void
ht_zonk_out(SCM * table)78 ht_zonk_out (SCM *table)
79 {
80   gi_gc_unprotect (*table);
81   gi_hash_clear_x (*table);
82   *table = SCM_BOOL_F;
83 }
84 
85 #define _CTYPE(ctype,x)     guile_svz_ ## ctype ## _ ## x
86 #define NAME_TAG(ctype)     _CTYPE (ctype, tag)
87 #define NAME_PRINT(ctype)   _CTYPE (ctype, print)
88 
89 #define integer_else(obj, def)                  \
90   (gi_exactp (obj)                              \
91    ? gi_scm2int (obj)                           \
92    : def)
93 
94 /* The guile server type hash.  */
95 static SCM all_servertypes;
96 
97 /* The guile socket hash.  */
98 static SCM all_sockets;
99 
100 /* If set to zero exception handling is disabled.  */
101 static int guile_use_exceptions = 1;
102 
103 /*
104  * Creates a Guile SMOB (small object).  The @var{ctype} specifies a base
105  * name for all defined vars and functions:
106  * a) tag     - The new scheme tag used to identify a SMOB.
107  * b) printer - Used when applying (display . args) in Guile.
108  */
109 #define MAKE_SMOB_DEFINITION(ctype)             \
110 static svz_smob_tag_t NAME_TAG (ctype);         \
111 static int NAME_PRINT (ctype)                   \
112      (SCM smob, SCM port,                       \
113       UNUSED scm_print_state *state)            \
114 {                                               \
115   static char txt[256];                         \
116                                                 \
117   snprintf (txt, 256, "#<svz-%s %p>", #ctype,   \
118             gi_smob_data (smob));               \
119   scm_puts (txt, port);                         \
120   return 1;                                     \
121 }
122 
123 /* Initializer macro for a new smob type.  */
124 #define INIT_SMOB(ctype)                        \
125   NAME_TAG (ctype) = gi_make_tag                \
126     ("svz-" #ctype,                             \
127      sizeof (svz_ ## ctype ## _t *),            \
128      NULL,                                      \
129      NAME_PRINT (ctype),                        \
130      NULL)
131 
132 /* Instantiating macro for a smob type.  */
133 #define MAKE_SMOB(ctype, data)  gi_make_smob (NAME_TAG (ctype), data)
134 
135 /* Checks if the given scheme cell is a smob or not.  */
136 #define CHECK_SMOB(ctype, smob)  gi_smob_tagged_p (smob, NAME_TAG (ctype))
137 
138 /* Checks if the scheme cell @var{smob} is a smob and throws an error if
139    not.  Otherwise the variable @var{var} receives the smob data.  */
140 #define CHECK_SMOB_ARG(ctype, smob, arg, description, var) do { \
141   if (!CHECK_SMOB (ctype, smob))                                \
142     scm_wrong_type_arg_msg (FUNC_NAME, arg, smob, description); \
143   else                                                          \
144     var = gi_smob_data (smob);                                  \
145   } while (0)
146 
147 /* Finally: With the help of the above macros we create smob types for
148    Serveez socket structures, servers and server types.  */
149 MAKE_SMOB_DEFINITION (socket)
150 MAKE_SMOB_DEFINITION (server)
151 MAKE_SMOB_DEFINITION (servertype)
152 
153 
154 /* A hash of smobs, keyed by the wrapped (C lang) object, a pointer.
155    This is consulted by ‘valid_smob’ and modified destructively by
156    ‘invalidate_smob’.  */
157 static SCM goodstuff;
158 
159 #define GOODSTUFF(key)  scm_hashq_ref (goodstuff, key, SCM_BOOL_F)
160 
161 static SCM
valid_smob(svz_smob_tag_t tag,void * orig)162 valid_smob (svz_smob_tag_t tag, void *orig)
163 {
164   SCM key = gi_gc_protect (PACK_POINTER (orig));
165   SCM val = GOODSTUFF (key);
166 
167   if (! gi_nfalsep (val))
168     {
169       val = gi_gc_protect (gi_make_smob (tag, orig));
170       scm_hashq_set_x (goodstuff, key, val);
171       gi_gc_unprotect (val);
172     }
173   gi_gc_unprotect (key);
174   return val;
175 }
176 
177 #define VALID_SMOB(ctype,orig)  valid_smob (NAME_TAG (ctype), orig)
178 
179 static void
invalidate_smob(const void * orig)180 invalidate_smob (const void *orig)
181 {
182   SCM key = PACK_POINTER (orig);
183   SCM smob = GOODSTUFF (key);
184 
185   if (gi_nfalsep (smob))
186     {
187       SCM_SET_SMOB_DATA (smob, NULL);
188       scm_hashq_remove_x (goodstuff, key);
189     }
190 }
191 
192 static void
invalidate_socket_smob(const svz_socket_t * sock)193 invalidate_socket_smob (const svz_socket_t *sock)
194 {
195   invalidate_smob (sock);
196 }
197 
198 static SCM
socket_smob(svz_socket_t * orig)199 socket_smob (svz_socket_t *orig)
200 {
201   return VALID_SMOB (socket, orig);
202 }
203 
204 static SCM
server_smob(svz_server_t * orig)205 server_smob (svz_server_t *orig)
206 {
207   return VALID_SMOB (server, orig);
208 }
209 
210 static SCM
servertype_smob(svz_servertype_t * orig)211 servertype_smob (svz_servertype_t *orig)
212 {
213   return VALID_SMOB (servertype, orig);
214 }
215 
216 /*
217  * Extract a guile procedure from an option hash.  Return zero on success.
218  */
219 static int
optionhash_extract_proc(svz_hash_t * hash,enum guile_functions_ix kidx,SCM * target,char * txt)220 optionhash_extract_proc (svz_hash_t *hash,
221                          enum guile_functions_ix kidx, /* the key to find       */
222                          SCM *target,      /* where to put it       */
223                          char *txt)        /* appended to error     */
224 {
225   char key[32];
226   SCM proc, hvalue;
227   int err = 0;
228 
229 #define BADNESS(fmtstr, ...)  do                \
230     {                                           \
231       err = 1;                                  \
232       guile_error (fmtstr, __VA_ARGS__);        \
233     }                                           \
234   while (0)
235 
236   GI_GET_XREP (key, guile_functions[kidx].sym);
237   hvalue = optionhash_get (hash, key);
238 
239   /* Is there such a string in the option-hash?  */
240   if (SCM_EQ_P (hvalue, SCM_UNSPECIFIED))
241     {
242       /* Nothing in hash, use default.  */
243       *target = SCM_UNDEFINED;
244       return err;
245     }
246 
247   /* Is that guile procedure?  */
248   if (SCM_PROCEDUREP (hvalue))
249     {
250       *target = hvalue;
251     }
252   else if (STRING_OR_SYMBOL_P (hvalue))
253     {
254       char str[128];
255 
256       GI_GET_XREP (str, hvalue);
257       proc = gi_lookup (str);
258       if (BOUNDP (proc) && SCM_PROCEDUREP (proc))
259         *target = proc;
260       else
261         BADNESS ("No such procedure `%s' for `%s' %s", str, key, txt);
262     }
263   else
264     BADNESS ("Invalid procedure for `%s' %s", key, txt);
265   return err;
266 
267 #undef BADNESS
268 }
269 
270 /*
271  * Return the procedure associated w/ the @var{fidx} entry
272  * in @code{guile_functions} for @var{stype},
273  * or ‘SCM_UNDEFINED’ on lookup failure.
274  */
275 static SCM
servertype_getfunction(svz_servertype_t * stype,enum guile_functions_ix fidx)276 servertype_getfunction (svz_servertype_t *stype,
277                         enum guile_functions_ix fidx)
278 {
279   SCM servertype, functions;
280 
281   if (stype == NULL)
282     return SCM_UNDEFINED;
283 
284   servertype = servertype_smob (stype);
285   functions = scm_hashq_ref (all_servertypes, servertype, SCM_BOOL_F);
286   if (! gi_nfalsep (functions))
287     return SCM_UNDEFINED;
288 
289   return scm_hashq_ref (functions, guile_functions[fidx].sym, SCM_UNDEFINED);
290 }
291 
292 /*
293  * Do ‘servertype_getfunction’ for @var{server}.
294  */
295 static SCM
server_getfunction(svz_server_t * server,enum guile_functions_ix fidx)296 server_getfunction (svz_server_t *server, enum guile_functions_ix fidx)
297 {
298   return servertype_getfunction (server->type, fidx);
299 }
300 
301 /*
302  * Return the procedure associated w/ the @var{ix} entry
303  * in @code{guile_sock_fns} for @var{sock},
304  * or ‘SCM_UNDEFINED’ on lookup failure.
305  */
306 static SCM
guile_sock_getfunction(svz_socket_t * sock,enum guile_sock_fns_ix ix)307 guile_sock_getfunction (svz_socket_t *sock, enum guile_sock_fns_ix ix)
308 {
309   SCM ssock, functions;
310 
311   if (sock == NULL)
312     return SCM_UNDEFINED;
313 
314   if (svz_sock_find (sock->id, sock->version) != sock)
315     return SCM_UNDEFINED;
316 
317   ssock = socket_smob (sock);
318   functions = scm_hashq_ref (all_sockets, ssock, SCM_BOOL_F);
319   if (! gi_nfalsep (functions))
320     return SCM_UNDEFINED;
321 
322   return scm_hashq_ref (functions, guile_sock_fns[ix].sym, SCM_UNDEFINED);
323 }
324 
325 extern int global_exit_value;
326 
327 SCM_DEFINE
328 (guile_nuke_happened,
329  "serveez-nuke", 0, 1, 0,
330  (SCM exit_value),
331  doc: /***********
332 Shutdown all network connections and terminate after the next event
333 loop.  You should use this instead of calling @code{quit}.
334 Optional arg @var{exit-value} specifies an exit value for the
335 serveez program.  It is mapped to a number via @code{scm_exit_value}.  */)
336 {
337 #define FUNC_NAME s_guile_nuke_happened
338   if (BOUNDP (exit_value))
339     /* The ‘cons’ avoids a segfault in Guile 1.8.7.  */
340     global_exit_value = scm_exit_status (scm_cons (exit_value, SCM_EOL));
341 
342   raise (SIGQUIT);
343   return SCM_UNSPECIFIED;
344 #undef FUNC_NAME
345 }
346 
347 SCM_DEFINE
348 (guile_access_exceptions,
349  "serveez-exceptions", 0, 1, 0,
350  (SCM enable),
351  doc: /***********
352 Control the use of exception handlers for the Guile procedure calls of
353 Guile server callbacks.  If the optional argument @var{enable} is
354 @code{#t}, enable exception handling; if @code{#f}, disable it.
355 Return the current (boolean) setting.  */)
356 {
357 #define FUNC_NAME s_guile_access_exceptions
358   SCM value = SCM_BOOL (guile_use_exceptions);
359   int n;
360 
361   if (BOUNDP (enable))
362     {
363       if (guile_to_boolean (enable, &n))
364         guile_error ("%s: Invalid boolean value", FUNC_NAME);
365       else
366         guile_use_exceptions = n;
367     }
368   return value;
369 #undef FUNC_NAME
370 }
371 
372 /*
373  * The @code{guile_call} function puts the procedure to call including
374  * the arguments to it into a single scheme cell passed to this function
375  * in @var{data}.  The functions unpacks this cell and applies it to
376  * @code{scm_apply}.
377  * By convention the @var{data} argument cell consists of three items chained
378  * like this: @code{(procedure first-argument (remaining-argument-list))}
379  */
380 static SCM
guile_call_body(void * data)381 guile_call_body (void *data)
382 {
383   SCM ls = SVZ_PTR2SCM (data);
384 
385   return scm_apply (SCM_CAR (ls), SCM_CADR (ls), SCM_CDDR (ls));
386 }
387 
388 /*
389  * This is the exception handler for calls by @code{guile_call}.  Prints
390  * the procedure (passed in @var{data}), the name of the exception and the
391  * error message if possible.
392  */
393 static SCM
guile_call_handler(void * data,SCM tag,SCM args)394 guile_call_handler (void *data, SCM tag, SCM args)
395 {
396   SCM procname = SVZ_PTR2SCM (data);
397   SCM ep = scm_current_error_port ();
398 
399   scm_puts ("exception in ", ep);
400   scm_display (procname, ep);
401   scm_puts (" due to `", ep);
402   scm_display (tag, ep);
403   scm_puts ("'\n", ep);
404   scm_puts ("guile-error: ", ep);
405 
406   /* on quit/exit */
407   if (SCM_NULLP (args))
408     {
409       scm_display (tag, ep);
410       scm_puts ("\n", ep);
411       return SCM_BOOL_F;
412     }
413 
414   if (!SCM_FALSEP (SCM_CAR (args)))
415     {
416       scm_display (SCM_CAR (args), ep);
417       scm_puts (": ", ep);
418     }
419   scm_display_error_message (SCM_CAR (SCM_CDR (args)),
420                              SCM_CAR (SCM_CDR (SCM_CDR (args))),
421                              ep);
422   return SCM_BOOL_F;
423 }
424 
425 /*
426  * The following function takes an arbitrary number of arguments (specified
427  * in @var{args}) passed to @code{scm_apply} calling the guile procedure
428  * @var{code}.  The function catches exceptions occurring in the procedure
429  * @var{code}.  On success (no exception) the routine returns the value
430  * returned by @code{scm_apply} otherwise @code{SCM_BOOL_F}.
431  */
432 static SCM
guile_call(SCM code,int args,...)433 guile_call (SCM code, int args, ...)
434 {
435   va_list list;
436   void *body_data, *handler_data;
437   SCM arg = SCM_EOL, arglist = SCM_EOL, ret;
438 
439   /* Setup arg and arglist correctly for use with ‘scm_apply’.  */
440   va_start (list, args);
441   if (args > 0)
442     {
443       arg = va_arg (list, SCM);
444       while (--args)
445         arglist = scm_cons (va_arg (list, SCM), arglist);
446       arglist = scm_cons (scm_reverse (arglist), SCM_EOL);
447     }
448   va_end (list);
449 
450   /* Put both arguments and the procedure together into a single argument
451      for the catch body.  */
452   body_data = SVZ_NUM2PTR (scm_cons (code, scm_cons (arg, arglist)));
453   handler_data = SVZ_NUM2PTR (code);
454 
455   /* Use exception handling if requested.  */
456   if (guile_use_exceptions)
457     {
458       ret = scm_internal_catch (SCM_BOOL_T,
459                                 guile_call_body, body_data,
460                                 guile_call_handler, handler_data);
461     }
462   else
463     {
464       ret = guile_call_body (body_data);
465     }
466 
467   return ret;
468 }
469 
470 /* Wrapper function for the global initialization of a server type.  */
471 static int
guile_func_global_init(svz_servertype_t * stype)472 guile_func_global_init (svz_servertype_t *stype)
473 {
474 #define FUNC_NAME __func__
475   SCM global_init = servertype_getfunction (stype, fn_global_init);
476   SCM ret;
477 
478   if (BOUNDP (global_init))
479     {
480       ret = guile_call (global_init, 1, servertype_smob (stype));
481       return integer_else (ret, -1);
482     }
483   return 0;
484 #undef FUNC_NAME
485 }
486 
487 
488 /* Wrapper function for the initialization of a server instance.  */
489 static int
guile_func_init(svz_server_t * server)490 guile_func_init (svz_server_t *server)
491 {
492 #define FUNC_NAME __func__
493   SCM init = server_getfunction (server, fn_init);
494   SCM ret;
495 
496   if (BOUNDP (init))
497     {
498       ret = guile_call (init, 1, server_smob (server));
499       return integer_else (ret, -1);
500     }
501   return 0;
502 #undef FUNC_NAME
503 
504 }
505 /* Wrapper routine for protocol detection of a server instance.  */
506 static int
guile_func_detect_proto(svz_server_t * server,svz_socket_t * sock)507 guile_func_detect_proto (svz_server_t *server, svz_socket_t *sock)
508 {
509 #define FUNC_NAME __func__
510   SCM detect_proto = server_getfunction (server, fn_detect_proto);
511   SCM ret;
512 
513   if (BOUNDP (detect_proto))
514     {
515       ret = guile_call (detect_proto, 2, server_smob (server),
516                         socket_smob (sock));
517       return integer_else (ret, 0);
518     }
519   return 0;
520 }
521 #undef FUNC_NAME
522 
523 /* Free the socket boundary if set by guile.  */
524 static void
guile_sock_clear_boundary(svz_socket_t * sock)525 guile_sock_clear_boundary (svz_socket_t *sock)
526 {
527   if (sock->boundary)
528     svz_free_and_zero (sock->boundary);
529   sock->boundary_size = 0;
530 }
531 
532 /* Wrapper for the socket disconnected callback.  Used here in order to
533    delete the additional guile callbacks associated with the disconnected
534    socket structure.  */
535 static int
guile_func_disconnected_socket(svz_socket_t * sock)536 guile_func_disconnected_socket (svz_socket_t *sock)
537 {
538 #define FUNC_NAME __func__
539   SCM ssock = socket_smob (sock);
540   SCM ret, disconnected = guile_sock_getfunction (sock, sfn_disconnected);
541   int retval = -1;
542 
543   /* First call the guile callback if necessary.  */
544   if (BOUNDP (disconnected))
545     {
546       ret = guile_call (disconnected, 1, ssock);
547       retval = integer_else (ret, -1);
548     }
549 
550   /* Delete all the associated guile callbacks.  */
551   scm_hashq_remove_x (all_sockets, ssock);
552 
553   /* Free the socket boundary if set by guile.  */
554   guile_sock_clear_boundary (sock);
555 
556   return retval;
557 #undef FUNC_NAME
558 
559 }
560 /* Wrapper for the kicked socket callback.  */
561 static int
guile_func_kicked_socket(svz_socket_t * sock,int reason)562 guile_func_kicked_socket (svz_socket_t *sock, int reason)
563 {
564 #define FUNC_NAME __func__
565   SCM ret, kicked = guile_sock_getfunction (sock, sfn_kicked);
566 
567   if (BOUNDP (kicked))
568     {
569       ret = guile_call (kicked, 2, socket_smob (sock),
570                         gi_integer2scm (reason));
571       return integer_else (ret, -1);
572     }
573   return 0;
574 }
575 #undef FUNC_NAME
576 
577 /* Wrapper function for the socket connection after successful detection.  */
578 static int
guile_func_connect_socket(svz_server_t * server,svz_socket_t * sock)579 guile_func_connect_socket (svz_server_t *server, svz_socket_t *sock)
580 {
581 #define FUNC_NAME __func__
582   SCM connect_socket = server_getfunction (server, fn_connect_socket);
583   SCM ret;
584 
585   /* Setup this function for later use.  */
586   sock->disconnected_socket = guile_func_disconnected_socket;
587 
588   if (BOUNDP (connect_socket))
589     {
590       ret = guile_call (connect_socket, 2, server_smob (server),
591                         socket_smob (sock));
592       return integer_else (ret, 0);
593     }
594   return 0;
595 #undef FUNC_NAME
596 
597 }
598 /* Wrapper for the finalization of a server instance.  */
599 static int
guile_func_finalize(svz_server_t * server)600 guile_func_finalize (svz_server_t *server)
601 {
602 #define FUNC_NAME __func__
603   SCM ret, finalize = server_getfunction (server, fn_finalize);
604   int retval = 0;
605 
606   if (BOUNDP (finalize))
607     {
608       ret = guile_call (finalize, 1, server_smob (server));
609       retval = integer_else (ret, -1);
610     }
611 
612   invalidate_smob (server);
613   return retval;
614 }
615 #undef FUNC_NAME
616 
617 /* Wrapper routine for the global finalization of a server type.  */
618 static int
guile_func_global_finalize(svz_servertype_t * stype)619 guile_func_global_finalize (svz_servertype_t *stype)
620 {
621 #define FUNC_NAME __func__
622   svz_config_prototype_t *prototype = &stype->config_prototype;
623   SCM servertype = servertype_smob (stype);
624   SCM ret, global_finalize;
625   int i, retval = 0;
626 
627   global_finalize = servertype_getfunction (stype, fn_global_finalize);
628 
629   if (BOUNDP (global_finalize))
630     {
631       ret = guile_call (global_finalize, 1, servertype);
632       retval = integer_else (ret, -1);
633     }
634 
635   scm_hashq_remove_x (all_servertypes, servertype);
636   invalidate_smob (stype);
637   svz_free (stype->prefix);
638   svz_free (stype->description);
639   for (i = 0; prototype->items[i].type != SVZ_ITEM_END; i++)
640     svz_free (prototype->items[i].name);
641   svz_config_free (prototype, prototype->start);
642   svz_free (prototype->items);
643   svz_free (stype);
644   return retval;
645 #undef FUNC_NAME
646 }
647 
648 /* Wrapper for the client info callback.  */
649 static char *
guile_func_info_client(svz_server_t * server,svz_socket_t * sock)650 guile_func_info_client (svz_server_t *server, svz_socket_t *sock)
651 {
652 #define FUNC_NAME __func__
653   SCM info_client = server_getfunction (server, fn_info_client);
654   SCM ret;
655   static char text[1024];
656 
657   if (BOUNDP (info_client))
658     {
659       ret = guile_call (info_client, 2, server_smob (server),
660                         socket_smob (sock));
661       if (GI_GET_XREP_MAYBE (text, ret))
662         return text;
663     }
664   return NULL;
665 }
666 #undef FUNC_NAME
667 
668 /* Wrapper for the server info callback.  */
669 static char *
guile_func_info_server(svz_server_t * server)670 guile_func_info_server (svz_server_t *server)
671 {
672 #define FUNC_NAME __func__
673   SCM info_server = server_getfunction (server, fn_info_server);
674   SCM ret;
675   static char text[1024];
676 
677   if (BOUNDP (info_server))
678     {
679       ret = guile_call (info_server, 1, server_smob (server));
680       if (GI_GET_XREP_MAYBE (text, ret))
681         return text;
682     }
683   return NULL;
684 #undef FUNC_NAME
685 
686 }
687 /* Wrapper for the server notifier callback.  */
688 static int
guile_func_notify(svz_server_t * server)689 guile_func_notify (svz_server_t *server)
690 {
691 #define FUNC_NAME __func__
692   SCM ret, notify = server_getfunction (server, fn_notify);
693 
694   if (BOUNDP (notify))
695     {
696       ret = guile_call (notify, 1, server_smob (server));
697       return integer_else (ret, -1);
698     }
699   return -1;
700 }
701 #undef FUNC_NAME
702 
703 /* Wrapper for the server reset callback.  */
704 static int
guile_func_reset(svz_server_t * server)705 guile_func_reset (svz_server_t *server)
706 {
707 #define FUNC_NAME __func__
708   SCM ret, reset = server_getfunction (server, fn_reset);
709 
710   if (BOUNDP (reset))
711     {
712       ret = guile_call (reset, 1, server_smob (server));
713       return integer_else (ret, -1);
714     }
715   return -1;
716 #undef FUNC_NAME
717 
718 }
719 /* Wrapper for the socket check request callback.  */
720 static int
guile_func_check_request(svz_socket_t * sock)721 guile_func_check_request (svz_socket_t *sock)
722 {
723 #define FUNC_NAME __func__
724   SCM ret, check_request;
725   check_request = guile_sock_getfunction (sock, sfn_check_request);
726 
727   if (BOUNDP (check_request))
728     {
729       ret = guile_call (check_request, 1, socket_smob (sock));
730       return integer_else (ret, -1);
731     }
732   return -1;
733 }
734 #undef FUNC_NAME
735 
736 /* Wrapper for the socket handle request callback.  The function searches for
737    both the servertype specific and socket specific procedure.  */
738 static int
guile_func_handle_request(svz_socket_t * sock,char * request,int len)739 guile_func_handle_request (svz_socket_t *sock, char *request, int len)
740 {
741 #define FUNC_NAME __func__
742   svz_server_t *server;
743   SCM ret, handle_request;
744   handle_request = guile_sock_getfunction (sock, sfn_handle_request);
745 
746   if (!BOUNDP (handle_request))
747     {
748       server = svz_server_find (sock->cfg);
749       handle_request = server_getfunction (server, fn_handle_request);
750     }
751 
752   if (BOUNDP (handle_request))
753     {
754       ret = guile_call (handle_request, 3, socket_smob (sock),
755                         guile_data_to_bin (request, len), gi_integer2scm (len));
756       return integer_else (ret, -1);
757     }
758   return -1;
759 #undef FUNC_NAME
760 
761 }
762 /* Wrapper for the socket idle func callback.  */
763 static int
guile_func_idle_func(svz_socket_t * sock)764 guile_func_idle_func (svz_socket_t *sock)
765 {
766 #define FUNC_NAME __func__
767   SCM ret, idle_func = guile_sock_getfunction (sock, sfn_idle);
768 
769   if (BOUNDP (idle_func))
770     {
771       ret = guile_call (idle_func, 1, socket_smob (sock));
772       return integer_else (ret, -1);
773     }
774   return 0;
775 }
776 #undef FUNC_NAME
777 
778 /* Wrapper for the socket trigger condition func callback.  */
779 static int
guile_func_trigger_cond(svz_socket_t * sock)780 guile_func_trigger_cond (svz_socket_t *sock)
781 {
782 #define FUNC_NAME __func__
783   SCM ret, trigger_cond = guile_sock_getfunction (sock, sfn_trigger_condition);
784 
785   if (BOUNDP (trigger_cond))
786     {
787       ret = guile_call (trigger_cond, 1, socket_smob (sock));
788       return gi_nfalsep (ret);
789     }
790   return 0;
791 #undef FUNC_NAME
792 
793 }
794 /* Wrapper for the socket trigger func callback.  */
795 static int
guile_func_trigger_func(svz_socket_t * sock)796 guile_func_trigger_func (svz_socket_t *sock)
797 {
798 #define FUNC_NAME __func__
799   SCM ret, trigger_func = guile_sock_getfunction (sock, sfn_trigger);
800 
801   if (BOUNDP (trigger_func))
802     {
803       ret = guile_call (trigger_func, 1, socket_smob (sock));
804       return integer_else (ret, -1);
805     }
806   return 0;
807 }
808 #undef FUNC_NAME
809 
810 /* Wrapper for the socket check oob request callback.  */
811 static int
guile_func_check_request_oob(svz_socket_t * sock)812 guile_func_check_request_oob (svz_socket_t *sock)
813 {
814 #define FUNC_NAME __func__
815   SCM ret, check_request_oob;
816   check_request_oob = guile_sock_getfunction (sock, sfn_check_oob_request);
817 
818   if (BOUNDP (check_request_oob))
819     {
820       ret = guile_call (check_request_oob, 2, socket_smob (sock),
821                         gi_integer2scm (sock->oob));
822       return integer_else (ret, -1);
823     }
824   return -1;
825 #undef FUNC_NAME
826 }
827 
828 
829 /*
830  * sock callbacks: {handle,check}-request
831  * (and others)
832  */
833 
834 #define CHECK_SOCK_SMOB_ARG(svar,cvar)                          \
835   CHECK_SMOB_ARG (socket, svar, SCM_ARG1, "svz-socket", cvar)
836 
837 typedef int (* getset_fn_t) ();
838 
839 struct sock_callback_details {
840   const char  *who;
841   getset_fn_t  getset;
842   size_t       place;                   /* ‘svz_socket_t’ offset */
843   enum guile_sock_fns_ix assoc;
844 };
845 
846 static SCM
sock_callback_body(const struct sock_callback_details * d,SCM sock,SCM proc)847 sock_callback_body (const struct sock_callback_details *d,
848                     SCM sock, SCM proc)
849 {
850   const char *FUNC_NAME = d->who;
851   svz_socket_t *xsock;
852 
853   CHECK_SOCK_SMOB_ARG (sock, xsock);
854   if (BOUNDP (proc))
855     {
856       SCM func = guile_sock_fns[d->assoc].sym;
857       getset_fn_t *place = (void *) xsock + d->place;
858       SCM functions;
859       SCM oldproc;
860 
861       SCM_ASSERT_TYPE (SCM_PROCEDUREP (proc), proc, SCM_ARG2,
862                        FUNC_NAME, "procedure");
863       *place = d->getset;
864 
865       functions = scm_hashq_ref (all_sockets, sock, SCM_BOOL_F);
866       if (! gi_nfalsep (functions))
867         {
868           functions = protected_ht (3);
869           scm_hashq_set_x (all_sockets, sock, functions);
870           gi_gc_unprotect (functions);
871         }
872 
873       /* Put the procedure into the socket's hash.
874          If there was one previously set, return that.  */
875       oldproc = scm_hashq_ref (functions, func, SCM_BOOL_F);
876       if (! gi_nfalsep (oldproc))
877         oldproc = SCM_UNDEFINED;
878       scm_hashq_set_x (functions, func, proc);
879       return oldproc;
880     }
881   return guile_sock_getfunction (xsock, d->assoc);
882 }
883 
884 /* This macro creates a the body of socket callback getter/setter for
885    use from Scheme code.  The procedure returns any previously set
886    callback or an undefined value.  */
887 #define SOCK_CALLBACK_BODY(FUNC,ASSOC)          \
888   const struct sock_callback_details d = {      \
889     .who    = s_guile_sock_ ## FUNC,            \
890     .getset = guile_func_ ## FUNC,              \
891     .place  = offsetof (svz_socket_t, FUNC),    \
892     .assoc  = ASSOC                             \
893   };                                            \
894   return sock_callback_body (&d, sock, proc)
895 
896 SCM_DEFINE
897 (guile_sock_handle_request,
898  "svz:sock:handle-request", 1, 1, 0,
899  (SCM sock, SCM proc),
900  doc: /***********
901 Set the @code{handle-request} member of the socket structure @var{sock}
902 to @var{proc}.  Return the previously set handler if there is any.  */)
903 {
904   SOCK_CALLBACK_BODY (handle_request, sfn_handle_request);
905 }
906 
907 SCM_DEFINE
908 (guile_sock_check_request,
909  "svz:sock:check-request", 1, 1, 0,
910  (SCM sock, SCM proc),
911  doc: /***********
912 Set the @code{check-request} member of the socket structure @var{sock}
913 to @var{proc}.  Return the previously handler if there is any.  */)
914 {
915   SOCK_CALLBACK_BODY (check_request, sfn_check_request);
916 }
917 
918 
919 SCM_DEFINE
920 (guile_sock_boundary,
921  "svz:sock:boundary", 2, 0, 0,
922  (SCM sock, SCM boundary),
923  doc: /***********
924 Setup the packet boundary of the socket @var{sock}.  The given string
925 value @var{boundary} can contain any kind of data.  If @var{boundary}
926 is an exact number, set up the socket to parse fixed sized packets.
927 More precisely, set the @code{check-request} callback of the given
928 socket structure @var{sock} to an internal routine which runs the
929 socket's @code{handle-request} callback when it detects a
930 complete packet specified by @var{boundary}.
931 
932 For instance, you can arrange for Serveez to pass the
933 @code{handle-request} procedure lines of text by calling
934 @code{(svz:sock:boundary sock "\n")}.  */)
935 {
936 #define FUNC_NAME s_guile_sock_boundary
937   svz_socket_t *xsock;
938 
939   CHECK_SOCK_SMOB_ARG (sock, xsock);
940   SCM_ASSERT_TYPE (gi_exactp (boundary) || gi_stringp (boundary),
941                    boundary, SCM_ARG2, FUNC_NAME, "string or exact");
942 
943   /* Release previously set boundaries.  */
944   guile_sock_clear_boundary (xsock);
945 
946   /* Setup for fixed sized packets.  */
947   if (gi_exactp (boundary))
948     {
949       xsock->boundary = NULL;
950       xsock->boundary_size = gi_scm2int (boundary);
951     }
952   /* Handle packet delimiters.  */
953   else
954     {
955       char buf[512];
956 
957       xsock->boundary_size = GI_GET_XREP (buf, boundary);
958       xsock->boundary = svz_strdup (buf);
959     }
960 
961   /* Only assign this callback for connection oriented protocols.  */
962   if (xsock->proto & (SVZ_PROTO_TCP | SVZ_PROTO_PIPE))
963     xsock->check_request = svz_sock_check_request;
964 
965   return SCM_BOOL_T;
966 #undef FUNC_NAME
967 }
968 
969 SCM_DEFINE
970 (guile_sock_floodprotect,
971  "svz:sock:floodprotect", 1, 1, 0,
972  (SCM sock, SCM flag),
973  doc: /***********
974 Set or unset the flood protection bit of the given socket @var{sock}.
975 Return the previous value of this bit (@code{#t} or @code{#f}).  The
976 @var{flag} argument must be either boolean or an exact number and is
977 optional.  */)
978 {
979 #define FUNC_NAME s_guile_sock_floodprotect
980   svz_socket_t *xsock;
981   int flags;
982 
983   CHECK_SOCK_SMOB_ARG (sock, xsock);
984   flags = xsock->flags;
985   if (BOUNDP (flag))
986     {
987       SCM_ASSERT_TYPE (SCM_BOOLP (flag) || gi_exactp (flag),
988                        flag, SCM_ARG2, FUNC_NAME, "boolean or exact");
989       if ((SCM_BOOLP (flag) && gi_nfalsep (flag) != 0) ||
990           (gi_exactp (flag) && gi_scm2int (flag) != 0))
991         xsock->flags &= ~SVZ_SOFLG_NOFLOOD;
992       else
993         xsock->flags |= SVZ_SOFLG_NOFLOOD;
994     }
995   return (flags & SVZ_SOFLG_NOFLOOD) ? SCM_BOOL_F : SCM_BOOL_T;
996 #undef FUNC_NAME
997 }
998 
999 SCM_DEFINE
1000 (guile_sock_print,
1001  "svz:sock:print", 2, 0, 0,
1002  (SCM sock, SCM buffer),
1003  doc: /***********
1004 Write @var{buffer} (string or binary smob) to the socket @var{sock}.
1005 Return @code{#t} on success and @code{#f} on failure.  */)
1006 {
1007 #define FUNC_NAME s_guile_sock_print
1008   svz_socket_t *xsock;
1009   char tem[8192];
1010   char *buf;
1011   int len, ret = -1;
1012 
1013   CHECK_SOCK_SMOB_ARG (sock, xsock);
1014   SCM_ASSERT_TYPE (gi_stringp (buffer) || guile_bin_check (buffer),
1015                    buffer, SCM_ARG2, FUNC_NAME, "string or binary");
1016 
1017   if (gi_stringp (buffer))
1018     {
1019       len = GI_GET_XREP (tem, buffer);
1020       buf = tem;
1021     }
1022   else
1023     {
1024       buf = guile_bin_to_data (buffer, &len);
1025     }
1026 
1027   /* Depending on the protocol type use different kind of senders.  */
1028   if (xsock->proto & (SVZ_PROTO_TCP | SVZ_PROTO_PIPE))
1029     ret = svz_sock_write (xsock, buf, len);
1030   else if (xsock->proto & SVZ_PROTO_UDP)
1031     ret = svz_udp_write (xsock, buf, len);
1032   else if (xsock->proto & SVZ_PROTO_ICMP)
1033     ret = svz_icmp_write (xsock, buf, len);
1034 
1035   if (ret == -1)
1036     {
1037       svz_sock_schedule_for_shutdown (xsock);
1038       return SCM_BOOL_F;
1039     }
1040   return SCM_BOOL_T;
1041 #undef FUNC_NAME
1042 }
1043 
1044 /*
1045  * Convert the given value at @var{address} of the the type @var{type} into
1046  * a scheme cell.
1047  */
1048 static SCM
guile_config_convert(void * address,int type)1049 guile_config_convert (void *address, int type)
1050 {
1051   svz_portcfg_t *port;
1052   SCM ret = SCM_EOL;
1053 
1054   switch (type)
1055     {
1056     case SVZ_ITEM_INT:
1057       ret = gi_integer2scm (*(int *) address);
1058       break;
1059     case SVZ_ITEM_INTARRAY:
1060       ret = guile_intarray_to_guile (*(svz_array_t **) address);
1061       break;
1062     case SVZ_ITEM_STR:
1063       ret = gi_string2scm (*(char **) address);
1064       break;
1065     case SVZ_ITEM_STRARRAY:
1066       ret = guile_strarray_to_guile (*(svz_array_t **) address);
1067       break;
1068     case SVZ_ITEM_HASH:
1069       ret = guile_hash_to_guile (*(svz_hash_t **) address);
1070       break;
1071     case SVZ_ITEM_PORTCFG:
1072       if ((port = *(svz_portcfg_t **) address) != NULL)
1073         ret = gi_string2scm (port->name);
1074       break;
1075     case SVZ_ITEM_BOOL:
1076       ret = SCM_BOOL (*(int *) address);
1077       break;
1078     }
1079   return ret;
1080 }
1081 
1082 /* Checks if the given Guile object @var{smob} in position @var{arg} is a
1083    server or socket and throws an exception if not.  Otherwise it saves the
1084    server in the variable @var{var}.  */
1085 #define CHECK_SERVER_SMOB_ARG(smob, arg, var)                                \
1086   do {                                                                       \
1087     SCM_ASSERT_TYPE (CHECK_SMOB (server, smob) ||                            \
1088                      CHECK_SMOB (socket, smob), smob, arg,                   \
1089                      FUNC_NAME, "svz-server or svz-socket");                 \
1090     var = CHECK_SMOB (server, smob) ? gi_smob_data (smob) :                  \
1091       svz_server_find (((svz_socket_t *) gi_smob_data (smob))->cfg);         \
1092   } while (0)
1093 
1094 SCM_DEFINE
1095 (guile_server_config_ref,
1096  "svz:server:config-ref", 2, 0, 0,
1097  (SCM server, SCM key),
1098  doc: /***********
1099 Return the configuration item specified by @var{key} of the given server
1100 instance @var{server}.  You can pass this procedure a socket, too, in
1101 which case the appropriate server instance is looked up.  If the given
1102 string @var{key} is invalid (not defined in the configuration alist in
1103 @code{define-servertype!}), then return an empty list.  */)
1104 {
1105 #define FUNC_NAME s_guile_server_config_ref
1106   SCM ret = SCM_EOL;
1107   svz_server_t *xserver;
1108   int i;
1109   void *cfg, *address;
1110   char str[64];
1111   svz_config_prototype_t *prototype;
1112 
1113   CHECK_SERVER_SMOB_ARG (server, SCM_ARG1, xserver);
1114   ASSERT_STRING (2, key);
1115 
1116   GI_GET_XREP (str, key);
1117   cfg = xserver->cfg;
1118   prototype = &xserver->type->config_prototype;
1119 
1120   for (i = 0; prototype->items[i].type != SVZ_ITEM_END; i++)
1121     {
1122       if (strcmp (prototype->items[i].name, str) == 0)
1123         {
1124           address = (void *) ((unsigned long) cfg +
1125                               (unsigned long) prototype->items[i].address -
1126                               (unsigned long) prototype->start);
1127           ret = guile_config_convert (address, prototype->items[i].type);
1128           break;
1129         }
1130     }
1131   return ret;
1132 #undef FUNC_NAME
1133 }
1134 
1135 /*
1136  * Returns the length of a configuration item type, updates the configuration
1137  * item structure @var{item} and increases the @var{size} value if the
1138  * text representation @var{str} fits one of the item types understood by
1139  * Serveez.  Returns zero if there is no such type.
1140  */
1141 static int
guile_servertype_config_type(char * str,svz_key_value_pair_t * item,int * size)1142 guile_servertype_config_type (char *str, svz_key_value_pair_t *item, int *size)
1143 {
1144   int n;
1145   struct {
1146     char *key;
1147     int size;
1148     int type;
1149   }
1150   config_types[] = {
1151     { "integer", sizeof (int), SVZ_ITEM_INT },
1152     { "intarray", sizeof (svz_array_t *), SVZ_ITEM_INTARRAY },
1153     { "string", sizeof (char *), SVZ_ITEM_STR },
1154     { "strarray", sizeof (svz_array_t *), SVZ_ITEM_STRARRAY },
1155     { "hash", sizeof (svz_hash_t *), SVZ_ITEM_HASH },
1156     { "portcfg", sizeof (svz_portcfg_t *), SVZ_ITEM_PORTCFG },
1157     { "boolean", sizeof (int), SVZ_ITEM_BOOL },
1158     { NULL, 0, -1 }
1159   };
1160 
1161   for (n = 0; config_types[n].key != NULL; n++)
1162     {
1163       if (strcmp (str, config_types[n].key) == 0)
1164         {
1165           item->type = config_types[n].type;
1166           *size += config_types[n].size;
1167           return config_types[n].size;
1168         }
1169     }
1170   return 0;
1171 }
1172 
1173 /*
1174  * Obtain a default value from the scheme cell @var{value}.  The configuration
1175  * item type is specified by @var{type}.  The default value is stored then at
1176  * @var{address}.  Returns zero on success.
1177  */
1178 static int
guile_servertype_config_default(svz_servertype_t * stype,SCM value,void * address,int len,int type,char * key)1179 guile_servertype_config_default (svz_servertype_t *stype, SCM value,
1180                                  void *address, int len, int type, char *key)
1181 {
1182   int err = 0, n;
1183   char str[2048], *txt;
1184   svz_array_t *array;
1185   svz_hash_t *hash;
1186   svz_portcfg_t *port, *dup;
1187 
1188   switch (type)
1189     {
1190       /* Integer.  */
1191     case SVZ_ITEM_INT:
1192       if (guile_to_integer (value, &n) != 0)
1193         {
1194           guile_error ("%s: Invalid integer value for `%s'",
1195                        stype->prefix, key);
1196           err = -1;
1197         }
1198       else
1199         memcpy (address, &n, len);
1200       break;
1201 
1202       /* Array of integers.  */
1203     case SVZ_ITEM_INTARRAY:
1204       if ((array = guile_to_intarray (value, key)) == NULL)
1205         err = -1;
1206       else
1207         memcpy (address, &array, len);
1208       break;
1209 
1210       /* Character string.  */
1211     case SVZ_ITEM_STR:
1212       if (! GI_GET_XREP_MAYBE (str, value))
1213         {
1214           guile_error ("%s: Invalid string value for `%s'",
1215                        stype->prefix, key);
1216           err = -1;
1217         }
1218       else
1219         {
1220           txt = svz_strdup (str);
1221           memcpy (address, &txt, len);
1222         }
1223       break;
1224 
1225       /* Array of character strings.  */
1226     case SVZ_ITEM_STRARRAY:
1227       if ((array = guile_to_strarray (value, key)) == NULL)
1228         err = -1;
1229       else
1230         memcpy (address, &array, len);
1231       break;
1232 
1233       /* Hash.  */
1234     case SVZ_ITEM_HASH:
1235       if ((hash = guile_to_hash (value, key)) == NULL)
1236         err = -1;
1237       else
1238         memcpy (address, &hash, len);
1239       break;
1240 
1241       /* Port configuration.  */
1242     case SVZ_ITEM_PORTCFG:
1243       if (! GI_GET_XREP_MAYBE (str, value))
1244         {
1245           guile_error ("%s: Invalid string value for `%s'",
1246                        stype->prefix, key);
1247           err = -1;
1248         }
1249       else if ((port = svz_portcfg_get (str)) == NULL)
1250         {
1251           guile_error ("%s: No such port configuration: `%s'",
1252                        stype->prefix, str);
1253           err = -1;
1254         }
1255       else
1256         {
1257           dup = svz_portcfg_dup (port);
1258           memcpy (address, &dup, len);
1259         }
1260       break;
1261 
1262       /* Boolean value.  */
1263     case SVZ_ITEM_BOOL:
1264       if (guile_to_boolean (value, &n) != 0)
1265         {
1266           guile_error ("%s: Invalid boolean value for `%s'",
1267                        stype->prefix, key);
1268           err = -1;
1269         }
1270       else
1271         memcpy (address, &n, sizeof (int));
1272       break;
1273 
1274       /* Invalid type.  */
1275     default:
1276       err = -1;
1277     }
1278   return err;
1279 }
1280 
1281 static void
items_append(svz_key_value_pair_t ** all,unsigned int i,svz_key_value_pair_t * one)1282 items_append (svz_key_value_pair_t **all, unsigned int i,
1283               svz_key_value_pair_t *one)
1284 {
1285   *all = svz_realloc (*all, (1 + i) * sizeof (*one));
1286   /* Is this equivalent to:  *((*all)[n]) = *one;  ???  */
1287   memcpy (&((*all)[i]), one, sizeof (*one));
1288 }
1289 
1290 struct servertype_config_closure
1291 {
1292   svz_servertype_t *stype;
1293   char *action;
1294   svz_hash_t *options;
1295   int error;
1296   int size;
1297   unsigned int count;
1298   svz_key_value_pair_t *items;
1299   char *prototype;
1300 };
1301 
1302 static void
servertype_config_internal(void * k,UNUSED void * v,void * closure)1303 servertype_config_internal (void *k, UNUSED void *v, void *closure)
1304 {
1305   struct servertype_config_closure *x = closure;
1306   char *key = k;
1307   SCM list = optionhash_get (x->options, key);
1308   svz_key_value_pair_t item;
1309   SCM value;
1310   char str[64];
1311   int len, def;
1312 
1313   /* Each configuration item must be a scheme list with three elements.  */
1314   if (!SCM_LISTP (list) ||
1315       gi_scm2ulong (scm_length (list)) != 3)
1316     {
1317       guile_error ("Invalid definition for `%s' %s", key, x->action);
1318       x->error = -1;
1319       return;
1320     }
1321 
1322   /* Assign address offset.  */
1323   item.address = SVZ_NUM2PTR (x->size);
1324 
1325   /* First appears the type of item.  */
1326   value = SCM_CAR (list);
1327   if (! GI_GET_XREP_MAYBE (str, value))
1328     {
1329       guile_error ("Invalid type definition for `%s' %s", key, x->action);
1330       x->error = -1;
1331       return;
1332     }
1333   len = guile_servertype_config_type (str, &item, &x->size);
1334   if (len == 0)
1335     {
1336       guile_error ("Invalid type for `%s' %s", key, x->action);
1337       x->error = -1;
1338       return;
1339     }
1340 
1341   /* Then appears a boolean value specifying if the configuration
1342      item is defaultable or not.  */
1343   list = SCM_CDR (list);
1344   value = SCM_CAR (list);
1345   if (guile_to_boolean (value, &def) != 0)
1346     {
1347       guile_error ("Invalid defaultable value for `%s' %s", key, x->action);
1348       x->error = -1;
1349       return;
1350     }
1351   item.defaultable = def
1352     ? SVZ_ITEM_DEFAULTABLE
1353     : SVZ_ITEM_NOTDEFAULTABLE;
1354 
1355   /* Finally the default value itself.  */
1356   list = SCM_CDR (list);
1357   value = SCM_CAR (list);
1358   x->prototype = svz_realloc (x->prototype, x->size);
1359   memset (x->prototype + x->size - len, 0, len);
1360   if (item.defaultable == SVZ_ITEM_DEFAULTABLE)
1361     {
1362       x->error |= guile_servertype_config_default
1363         (x->stype, value, x->prototype + x->size - len,
1364          len, item.type, key);
1365     }
1366 
1367   /* Increase the number of configuration items.  */
1368   item.name = svz_strdup (key);
1369   items_append (&x->items, x->count++, &item);
1370 }
1371 
1372 /*
1373  * Parse the configuration of the server type @var{stype} stored in the
1374  * scheme cell @var{cfg}.
1375  */
1376 static int
guile_servertype_config(svz_servertype_t * stype,SCM cfg)1377 guile_servertype_config (svz_servertype_t *stype, SCM cfg)
1378 {
1379 #define FUNC_NAME __func__
1380   unsigned int n;
1381   svz_key_value_pair_t item;
1382   char action[ACTIONBUFSIZE];
1383   struct servertype_config_closure x =
1384     {
1385       stype, action,
1386       NULL,                             /* .options */
1387       0,                                /* .error */
1388       0,                                /* .size */
1389       0,                                /* .count */
1390       NULL,                             /* .items */
1391       NULL                              /* .prototype */
1392     };
1393 
1394 #define err      (x.error)              /* Keep things tidy.  */
1395 #define items    (x.items)
1396 #define options  (x.options)
1397 
1398   DOING ("parsing configuration of `%s'", stype->prefix);
1399 
1400   /* Check if the configuration alist is given or not.  */
1401   if (SCM_EQ_P (cfg, SCM_UNSPECIFIED))
1402     {
1403       guile_error ("Missing servertype `configuration' for `%s'",
1404                    stype->prefix);
1405       FAIL ();
1406     }
1407 
1408   /* Try parsing this alist is valid.  */
1409   if (NULL == (options = guile_to_optionhash (cfg, action, 0)))
1410     FAIL ();                    /* Message already emitted.  */
1411 
1412   /* Check the servertype configuration definition for duplicates.  */
1413   err |= optionhash_validate (options, 1, "configuration", stype->prefix);
1414 
1415   /* Now check all configuration items.  */
1416   svz_hash_foreach (servertype_config_internal, options, &x);
1417 
1418   /* Append the last configuration item identifying the end of the
1419      configuration item list.  */
1420   item.type = SVZ_ITEM_END;
1421   item.address = NULL;
1422   item.defaultable = 0;
1423   item.name = NULL;
1424   items_append (&items, x.count++, &item);
1425 
1426   /* Adjust the address values of the configuration items and assign
1427      all gathered information to the given servertype.  */
1428   for (n = 0; n < x.count; n++)
1429     items[n].address = (void *) ((unsigned long) items[n].address +
1430       (unsigned long) x.prototype);
1431   stype->config_prototype.start = x.prototype;
1432   stype->config_prototype.size = x.size;
1433 #undef items        /* Unfortunately, tidy is incorrect for next line LHS.  */
1434   stype->config_prototype.items = x.items;
1435 
1436  out:
1437   optionhash_destroy (options);
1438   return err;
1439 
1440 #undef options
1441 #undef err
1442 #undef FUNC_NAME
1443 }
1444 
1445 SCM_DEFINE
1446 (guile_define_servertype,
1447  "define-servertype!", 1, 0, 0,
1448  (SCM args),
1449  doc: /***********
1450 Define a new server type based on @var{args}.  (If everything
1451 works fine you have a freshly registered server type afterwards.)
1452 Return @code{#t} on success.  */)
1453 {
1454 #define FUNC_NAME s_guile_define_servertype
1455   enum guile_functions_ix n;
1456   int err = 0;
1457   svz_hash_t *options;
1458   SCM proc, functions;
1459   svz_servertype_t *stype;
1460   char action[ACTIONBUFSIZE];
1461 
1462   stype = svz_calloc (sizeof (svz_servertype_t));
1463   DEFINING ("%s", "servertype");
1464 
1465   if (NULL == (options = guile_to_optionhash (args, action, 0)))
1466     FAIL ();                    /* Message already emitted.  */
1467 
1468   /* Obtain the servertype prefix variable (Mandatory).  */
1469   if (optionhash_extract_string (options, "prefix", 0, NULL,
1470                                  &stype->prefix, action) != 0)
1471     FAIL ();
1472   DEFINING ("servertype `%s'", stype->prefix);
1473 
1474   /* Check the servertype definition once.  */
1475   err |= optionhash_validate (options, 1, "servertype", stype->prefix);
1476 
1477   /* Get the description of the server type.  */
1478   err |= optionhash_extract_string (options, "description", 0, NULL,
1479                                     &stype->description, action);
1480 
1481   /* Set the procedures.  */
1482   functions = protected_ht (3);
1483   for (n = 0; n < guile_functions_count; n++)
1484     {
1485       err |= optionhash_extract_proc (options, n, &proc, action);
1486       scm_hashq_set_x (functions, guile_functions[n].sym, proc);
1487     }
1488 
1489   /* Check duplicate server types.  */
1490   if (svz_servertype_get (stype->prefix, 0) != NULL)
1491     {
1492       guile_error ("Duplicate servertype definition: `%s'", stype->prefix);
1493       err = -1;
1494     }
1495   else
1496     {
1497       /* Check the configuration items for this servertype.  */
1498       err |= guile_servertype_config (stype,
1499                                       optionhash_get (options,
1500                                                       "configuration"));
1501     }
1502 
1503   if (!err)
1504     {
1505       SCM servertype = servertype_smob (stype);
1506 
1507       stype->global_init = guile_func_global_init;
1508       stype->init = guile_func_init;
1509       stype->detect_proto = guile_func_detect_proto;
1510       stype->connect_socket = guile_func_connect_socket;
1511       stype->finalize = guile_func_finalize;
1512       stype->global_finalize = guile_func_global_finalize;
1513       stype->info_client = guile_func_info_client;
1514       stype->info_server = guile_func_info_server;
1515       stype->notify = guile_func_notify;
1516       stype->reset = guile_func_reset;
1517       stype->handle_request = guile_func_handle_request;
1518 
1519       /* Hook it all up.  */
1520       scm_hashq_set_x (all_servertypes, servertype, functions);
1521       gi_gc_unprotect (functions);
1522       svz_servertype_add (stype);
1523     }
1524   else
1525     {
1526       svz_free (stype->prefix);
1527       if (stype->description)
1528         svz_free (stype->description);
1529       svz_free (stype);
1530       ht_zonk_out (&functions);
1531     }
1532 
1533  out:
1534   optionhash_destroy (options);
1535   guile_global_error |= err;
1536   return err ? SCM_BOOL_F : SCM_BOOL_T;
1537 #undef FUNC_NAME
1538 }
1539 
1540 #include "guile-api.c"
1541 
1542 /*
1543  * Initialization of the guile server module.  Should be run before calling
1544  * @code{guile_eval_file}.  It registers some new guile procedures and
1545  * creates some static data.
1546  */
1547 void
guile_server_init(void)1548 guile_server_init (void)
1549 {
1550   INIT_SYMBOLSET (guile_functions);
1551   INIT_SYMBOLSET (guile_sock_fns);
1552 
1553   /* Initialize the guile SMOB things.  Previously defined via
1554      MAKE_SMOB_DEFINITION ().  */
1555   INIT_SMOB (socket);
1556   INIT_SMOB (server);
1557   INIT_SMOB (servertype);
1558 
1559   svz_sock_prefree (1, invalidate_socket_smob);
1560   goodstuff = protected_ht (11);
1561   all_servertypes = protected_ht (3);
1562   all_sockets = protected_ht (23);
1563 
1564 #include "guile-server.x"
1565 
1566   guile_bin_init ();
1567   guile_api_init ();
1568 }
1569 
1570 /*
1571  * This function should be called before shutting down the core library in
1572  * order to avoid memory leaks.  It releases the server types defined with
1573  * guile.
1574  */
1575 void
guile_server_finalize(void)1576 guile_server_finalize (void)
1577 {
1578   guile_api_finalize ();
1579 
1580   ht_zonk_out (&all_sockets);
1581   ht_zonk_out (&all_servertypes);
1582   ht_zonk_out (&goodstuff);
1583   svz_sock_prefree (0, invalidate_socket_smob);
1584 }
1585 
1586 #else /* not ENABLE_GUILE_SERVER */
1587 
1588 static int have_guile_server = 0;
1589 
1590 #endif /* ENABLE_GUILE_SERVER */
1591