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