6.3 Guile

Guile is an acronym for GNU's Ubiquitous Intelligent Language for Extensions. It provides a Scheme interpreter conforming to the R5RS language specification and a number of convenience functions. For information about the language, refer to (r5rs)Top section `Top' in Revised(5) Report on the Algorithmic Language Scheme. For a detailed description of Guile and its features, see (guile)Top section `Overview' in The Guile Reference Manual.

The guile module provides an interface to Guile which allows for writing Smap modules in Scheme. The module is loaded using the following configuration file statement:

 
module name guile [args]

Optional args are:

debug

Enable Guile debugging and stack traces.

nodebug

Disable Guile debugging and stack traces (default).

load-path=path

Append directories from path to the list of directories which should be searched for Scheme modules and libraries. The path must be a list of directory names, separated by colons.

This option modifies the value of Guile's %load-path variable. See the section Configuration and Installation in the Guile Reference Manual.

init-script=script

Specifies the name of a Scheme source file that must be loaded in order to initialize the module. The file is looked up using ‘%load-path’ variable.

init-args

The init-args parameter supplies additional arguments to the module. They will be accessible to the ‘script’ via the command-line function.

init-fun

This parameter specifies the name of a function that will be invoked to perform the initialization of the module and of particular databases. Default name is ‘init’. See section Guile Initialization, for a description of initialization sequence.

Guile databases are declared using the following syntax:

 
database dbname modname [args] [cmdline]

where: dbname gives the name for this database and modname is the name given to Guile module in module statement (see above).

Optional args override global settings given in the module statement. The following options are understood: init-script, init-args, and init-fun. Their meaning is the same as for module statement (see above), except that they affect only this particular database.

Any additional arguments, referenced as cmdline above, are be passed to the Guile open-db callback function (see open-db).

6.3.1 Virtual Functions

Any database handled by guile module is associated with a virtual function table. This table is an association list which supplies to the module the Scheme call-back functions implemented to perform particular tasks on that database. In this list, the car of each element contains the name of a function, and its cdr gives the corresponding function. The defined function names and their semantics are described in the following table:

init

Initialize the module.

done

Close the module, releasing any resources held by it.

open

Open the database.

close

Close the database.

query

Handle a socket map query

xform

Handle a transformation request (see section Transformations).

For example, the following is a valid virtual function table:

 
(list (cons "open" open-module)
      (cons "close" close-module)
      (cons "query" run-query))

Apart from per-database virtual tables, there is also a global virtual function table, which is used to supply the entries missing in the former. Both tables are created during the module initialization, as described in the next subsection.

Particular virtual functions are described in Guile API.

6.3.2 Guile Output Ports

Guile modules are executed in a specially prepared environment. Current error port is redirected so that everything written to it ends up in the smapd error stream. So, if smapd is writing its log to syslog, everything you write to ‘(current-error-port)’ will be written to syslog as well. The port is line-buffered. For example, the following code:

 
(with-output-to-port
  (current-error-port)
 (lambda ()
   (display "The diagnostics follows:")
   (newline)
   (display "Module opened")
   (newline)))

will result in two lines in your syslog file, which will look like

 
Jun 19 12:49:05 netbox smapd[7503]: The diagnostics follows
Jun 19 12:49:05 netbox smapd[7503]: Module opened

For any debugging output, use smap-debug-port. This port is configured so that everything written to it is explicitly marked as being debug output. If smapd logs to stderr, it will be prefixed with ‘DEBUG:’, and if it logs to syslog, the output will be logged with ‘LOG_DEBUG’ priority.

Finally, current output port is closed for any functions, excepting ‘query’ (see query-db). For ‘query’ function, it is redirected so that anything written to it is reformatted according to the socket map protocol (see section The Sockmap Protocol) and sent back as a reply to the client.

6.3.3 Guile Initialization

The module configuration statement causes loading and initialization of the guile module:

 
module modname guile [init-script=‘script’] \
                           [init-fun=function"]

Upon module initialization stage, the module attempts to load the file named ‘script’. The file is loaded using primitive-load-path call (see primitive-load-path: (guile)Loading section `Loading' in The Guile Reference Manual), i.e. it is searched in the Guile load path. The init-fun parameter supplies the name of the initialization function. This Scheme function returns virtual function tables for the module itself and for each database that uses this module. It must be declared as follows:

 
(define (function arg)
  …)

This function is called several times. First of all, it is called after script is loaded. This time it is given #f as its argument, and its return value is saved as a global function table. Then, it is called for each database statement that uses module modname (defined in the module statement above), e.g.:

 
database dbname modname

This time, it is given dbname as its argument and its return is stored as the virtual function table for this particular database.

The following example function returns a complete virtual function table:

 
(define (my-smap-init arg)
  (list (cons "init" db-init)
        (cons "done" db-done)
        (cons "open" db-open)
        (cons "close" db-close)
        (cons "query" db-query)
        (cons "xform" db-xform)))

6.3.4 Guile API

This subsection describes callback functions that a Guile database module must provide. The description of each function begins with the function prototype and its entry in the virtual function table.

Guile Callback: open-db name . args

Virtual table: (cons "open" open-db)

Open the database. The argument name contains database name as given in dbname of the database declaration (see section Databases). Optional argument args is a list of command line parameters obtained from cmdline in database statement (see guile-cmdline). For example, if the configuration file contained:

 
database foo guile db=file 1 no

then the open-db callback will be called as:

 
(open-db "foo" '("db=file" "1" "no"))

The open-db callback returns a database handle, i.e. an opaque Scheme object which identifies this database, and keeps its internal state. This value, hereinafter named dbh, will be passed to another callback functions that need to access the database.

The unspecified return value indicates an error.

Guile Callback: close-db dbh

Virtual Table: (cons "close" close-db)

Close the database. This function is called during the cleanup procedure, before termination of smapd child process. The argument dbh is a database handle returned by open-db.

The return value from close-db is ignored. To communicate errors to the daemon, throw an exception.

Guile Callback: query-db dbh map key . rest

Virtual Table: (cons "close" close-db)
Perform the query. Arguments are:

dbh

A database handle returned by open-db.

map

The map name.

key

The lookup key

rest

If this query came over a UNIX socket, this argument is ‘()’. Otherwise, if the query came over an INET socket, rest is a list of two network socket addresses (see (guile)Network Socket Address section `Network Socket Address' in The Guile Reference Manual): first element is the address of the remote party (client), second element is the address of the server that is handling the query.

This function must write the reply, terminated with a newline, to the current output port, e.g.:

 
(define-public (smap-query handle map arg . rest)
  (display "NOTFOUND")
  (newline))
Guile Callback: xform-db dbh arg . rest

Virtual Table: (cons "xform" xform-db)
Transform the argument arg. Arguments dbh and rest have the same meaning as in query-db.

Returns transformed value or ‘#f’ if no transformation applies. This callback may be used to alter map or key values using ‘guile’ module (see section Transformations). The following example function removes optional domain part from its argument:

 
(define (smap-xform handle arg . rest)
  (let ((arg-parts (string-split arg #\@)))
    (if (null? (cdr arg-parts))
	#f
	(car arg-parts))))

The following snippet from the ‘smapd.conf’ file shows how to apply it:

 
database localpart guile init-script=local.scm

dispatch key like *@* transform key localpart