Next: External I/O Loop, Previous: Client code, Up: Top [Contents][Index]
Implementing a server for Assuan is a bit more complex than a client. However, it is a straightforward task we are going to explain using a commented example.
The list of the implemented server commands is defined by a table like:
static struct { const char *name; int (*handler) (assuan_context_t, char *line); } command_table[] = { { "FOO", cmd_foo }, { "BAR", cmd_bar }, { "INPUT", NULL }, { "OUTPUT", NULL }, { NULL }};
For convenience this table is usually put after the actual command
handlers (cmd_foo
, cmd_bar
) or even put inside
command_handler
(see below). Note that the commands
INPUT
and OUTPUT
do not require a handler because
Libassuan provides a default handler for them. It is however possible
to assign a custom handler.
A prerequisite for this example code is that a client has already connected to the server. Often there are two modes combined in one program: A pipe-based server, where a client has forked the server process, or a Unix domain socket based server that is listening on the socket.
void command_handler (int fd) { gpg_error_t rc; int i; assuan_context_t ctx; rc = assuan_new (&ctx); if (rc) { fprintf (stderr, "server context creation failed: %s\n", gpg_strerror(rc)); return; } if (fd == -1) { assuan_fd_t filedes[2]; filedes[0] = assuan_fd_from_posix_fd (0); filedes[1] = assuan_fd_from_posix_fd (1); rc = assuan_init_pipe_server (ctx, filedes); } else rc = assuan_init_socket_server (ctx, fd, ASSUAN_SOCKET_SERVER_ACCEPTED); if (rc) { fprintf (stderr, "server init failed: %s\n", gpg_strerror (rc)); return; }
This is the first part of the command handler. We start off by
allocating a new Assuan context with assuan_new
.
See function assuan_new.
In case this is called as a pipe based server, fd will be based
as fd and the code assumes that the server’s stdin
and
stdout
file handles are connected to a pipe. The
initialization is thus done using the function:
This function takes the two file descriptors from filedes and
returns a new Assuan context at r_ctx. As usual, a return value
of 0
indicates success and a failure is indicated by
returning an error value. In case of error, NULL
will be stored
at r_ctx.
In case the server has been called using a bi-directional pipe
(socketpair), filedes is ignored and the file descriptor is
taken from the environment variable _assuan_connection_fd
. You
generally don’t need to know this, because assuan_pipe_connect
,
which is called by the client to connect to such a server,
automagically sets this variable.
This function takes the file descriptor fd, which is expected to be associated with a socket, and an Assuan context ctx. The following bits are currently defined for flags:
ASSUAN_SOCKET_SERVER_FDPASSING
If set, sendmsg
and recvmesg
are used for input and
output, which enables the use of descriptor passing.
ASSUAN_SOCKET_SERVER_ACCEPTED
If set, fd refers to an already accepted socket. That is, Libassuan won’t call accept for it. It is suggested to set this bit as it allows better control of the connection state.
As usual, a return value of 0
indicates success and a failure
is indicated by returning an error value.
On the Windows platform the following function needs to be called after
assuan_init_socket_server
:
Save a copy of nonce in context ctx. This should be used
to register the server’s nonce with a context established by
assuan_init_socket_server
. It is technically only needed for
Windows, but it does no harm to use it on other systems.
After error checking, the implemented assuan commands are registered with the server.
for (i = 0; command_table[i].name; i++) { rc = assuan_register_command (ctx, command_table[i].name, command_table[i].handler, NULL); if (rc) { fprintf (stderr, "register failed: %s\n", gpg_strerror (rc)); assuan_release (ctx); return; } }
This is the function invoked by ASSUAN for various command
related callback functions. Some of these callback functions have a
different type, but most use assuan_handler_t
.
This registers the command named cmd_string with the Assuan
context ctx. handler is the function called by Libassuan
if this command is received from the client. NULL may be used
for handler to use a default handler (this only works with a few
pre-defined commands). Note that several default handlers have
already been registered when the context has been created: NOP
,
CANCEL
, OPTION
, BYE
, AUTH
, RESET
and END
. It is possible, but not recommended, to override
these commands.
help_string is a help string that is used for automatic documentation. It should contain a usage line followed by an empty line and a complete description.
Register a function to be called right after a command has been processed. err is the result code from the last internal assuan operation and not the one returned by the handler. It may be used for command-related cleanup.
Register function fnc with context ctx to be called right
before the standard handler for the BYE
command is being called.
Register function fnc with context ctx to be called right
before the standard handler for the RESET
command is being called.
Register function fnc with context ctx to be called right
before the standard handler for the RESET
command is being called.
Register function fnc with context ctx for processing
options. That function is being called with the context, the name and
the value of the option. Leading and trailing spaces are removed from
the name and the value. The optional leading two dashes of the name
are removed as well. If no value has been given, an empty string is
passed. The function needs to return 0
on success or an error
code.
Although the input function may be overridden with a custom handler, it
is often more convenient to use the default handler and to know whether
an INPUT
command has been seen and successfully parsed. The second
argument passed to that function is the entire line. Because that line
has already been parsed when the function gets called, a file descriptor
set with the INPUT
command may already be used. That file
descriptor is available by calling assuan_get_input_fd
. If the
notification function returns an error, the input fd does not change.
Although the output function may be overridden with a custom handler, it
is often more convenient to use the default handler and to know whether
an OUTPUT
command has been seen and successfully parsed. The second
argument passed to that function is the entire line. Because that line
has already been parsed when the function gets called, a file descriptor
set with the OUTPUT
command may already be used. That file
descriptor is available by calling assuan_get_output_fd
. If the
notification function returns an error, the output fd does not change.
This is not actually a register function but may be called also after registering commands. It changes the “Hello” line, sent by the server to the client as a first response, from a default string to the string line. For logging purposes, it is often useful to use such a custom hello line which may tell version numbers and such. Linefeeds are allowed in this string, however, each line needs to be shorter than the Assuan line length limit.
Now that everything has been setup, we can start to process our clients requests.
for (;;) { rc = assuan_accept (ctx); if (rc == -1) break; else if (rc) { fprintf (stderr, "accept problem: %s\n", gpg_strerror (rc)); break; } rc = assuan_process (ctx); if (rc) { fprintf (stderr, "processing failed: %s\n", gpg_strerror (rc)); continue; } } assuan_release (ctx); }
For future extensibility and to properly detect the end of the connection the core of the server should loop over the accept and process calls.
A call to this function cancel any existing connection and waits for a
connection from a client (that might be skipped, depending on the type
of the server). The initial handshake is performed which may include an
initial authentication or encryption negotiation. On success 0
is returned. An error value will be returned if the connection could for
some reason not be established. An error code of GPG_ERR_EOF
indicates
the end of the connection.
This function is used to handle the Assuan protocol after a connection
has been established using assuan_accept
. It is the main
protocol handler responsible for reading the client commands and calling
the appropriate handlers. The function returns 0
on success or
an error value if something went seriously wrong. Error values from the
individual command handlers, i.e. operational error, are not seen here.
That is all needed for the server code. You only need to come up with the code for the individual command handlers. Take care that the line passed to the command handlers is allocated statically within the context and calls to Assuan functions may modify that line. You are also allowed to modify that line which makes parsing much easier.
Next: External I/O Loop, Previous: Client code, Up: Top [Contents][Index]