Previous: , Up: Writing servers   [Contents][Index]

3.2.3 Builtin servers

All of the servers listed in Existing servers are builtin servers. The following sections describe in detail how to setup a new builtin server type. This kind of server will be part of the Serveez executable. That is why you should make it configurable in the configure script via a ‘--enable-xxxserver’ argument. Making and configuring preparations

Serveez is configured and built via automake and autoconf. That is why you are not supposed to write your own Makefiles but simplified Makefile.ams. Automake will automatically generate dependencies and compiler/linker command lines. Here are the steps you basically need to follow: Server header file foo-proto.h

This file contains at least your server’s extern declaration of your server definition which must be available from the outside. The foo server implements all kinds of configuration items which can be integers, integer arrays, strings, string arrays, port configurations, booleans and hash maps. Every item of the server configuration can later be manipulated from the configuration file. Server implementation file foo-proto.c

If you want to define default values for your configuration you have to define them somewhere and put them into the default configuration structure. This structure will be used to instantiate your server. For this example we simply called it simply foo_config.

In order to associate the configuration items in a server configuration to keywords within the configuration file you have to define an array of key-value-pairs. This is done in the foo_config_prototype field. There are several macros which make different associations. These are the SVZ_REGISTER_* macros which take three arguments. The first argument is the keyword which will occur in the configuration file, the second is the associated item in your default configuration structure and the last argument specifies if this item is defaultable or not. Server definition

The server definition is in a way the ‘class’ of your server. Together with the default values (foo_config_prototype) it serves as a template for newly instantiated servers. The structure contains a long and a short description of your server. The short name is used as the prefix for all server instances of this specific type. The long description is used in the control protocol (See Control Protocol Server.). The server definition also contains the callbacks your server (mandatorily) provides. Server callbacks

There are several callback routines, which get called in order to instantiate the server and for describing the actual behaviour of your server. Here are the description of all of these callbacks. Some of them have to be implemented. Others have reasonable default values.

global initializer (optional)

This callback is executed once for every type of server. Here you can initialize data or whatever is shared between all instances of your server. For instance the HTTP server initializes its file cache here.

global finalizer (optional)

If you want to free shared resources which were possibly allocated within the global initializer you can do so here. The foo server frees its default hash previously allocated in the global initializer.

instance initializer (mandatory)

Within this routine you can initialize everything you might need for one instance of your server. The foo server does not do anything in this callback.

instance finalizer (optional)

The server instance finalizer gets its instance representation as argument. You have to free all resources used by this server instance.

protocol detection (mandatory)

Because it is possible to have more than one server listening on one network port we need to detect the type of client which is connecting to this port. The foo server checks if the first five bytes the client was sending is identifying it as a foo client. This routine is getting two arguments where the first one is a pointer to this servers instance and the second is the client socket object containing all information of the client connection. This structure is described a bit later. Be patient. For successful client detection return non-zero value.

socket connection (mandatory)

If the client detection signaled success this routine is called to assign the client connection to the server instance. The arguments are just the same as in the detection routine. In this callback you can assign all the connection specific callbacks for your server and do some initial things. The foo server sets the check_request callback to the default svz_sock_check_request which is using the packet delimiter information to find whole packets. When a client sent such a packet the handle_request callback is executed. That is why the foo server assigns the handle_request method.

client info (optional)

If this callback is given the control protocol (See Control Protocol Server.) can give information about a specific client if requested with ‘stat id NUM’. The first argument given is the server instance and the second one the client’s socket structure. You have to return a static single line character string.

server info (optional)

This function is called when listing the server instances via ‘stat all’ from the control protocol (See Control Protocol Server.). The returned character string might be multilined separated by \r\n (no trailing separator). Usually you will return all the server configuration information.

notifier (optional)

If this callback is not NULL it is called whenever there is some time left. It gets the server instance itself as argument. Actually it gets called every second.

handle request (mandatory for UDP and ICMP servers)

The arguments to this callback are the client’s socket structure, the address of the packet data and its length. When implementing a UDP or ICMP server you need to return non-zero if your server could process the packet. Thus it is possible that there are multiple UDP servers on a single port. Make your server available

You distribute your server by editing the cfgfile.c file in the src/ directory. There you have to include the servers header file and add the server definition by calling svz_servertype_add More detailed description of the callback system and structures

The client connection information is stored within the svz_socket_t object. All of the client connection specific callbacks get this object as first argument. Following is a description of the most important elements of this object.

int id

The socket id is a unique id for a client connection.

int version

This item validates this socket structure. If you pass the id and version to a coserver you can check if the delivered socket structure is the original or not within the coserver callback.

int proto

The proto flag determines a server sockets protocol type which can be PROTO_PIPE, PROTO_TCP, PROTO_UDP, PROTO_ICMP or PROTO_RAW.

int flags

The flag field of the client connection contains informations about the state of this connection. See socket.h in the src/libserveez/ directory for more information. Basically this bitfield specifies how this object is handled by the main server loop.

int userflags

This bitfield could be used for protocol specific information. You can use it for any information.

char *boundary, int boundary_size

If you are going to write a packet oriented protocol server you can use the svz_sock_check_request method to parse packets. These two properties describe the packet delimiter.

char *send_buffer, int send_buffer_size, int send_buffer_fill

This is the outgoing data for a client connection object.

char *recv_buffer, int recv_buffer_size, int recv_buffer_fill

Within the receive buffer all incoming data for a connection object is stored. This buffer is at least used for the client detection callback.

int read_socket (svz_socket_t)

This callback gets called whenever data is available on the socket. Normally, this is set to a default function which reads all available data from the socket and feeds it to check_request, but specific sockets may need other policies.

int write_socket (svz_socket_t)

This routine is called when data is is valid in the output buffer and the socket gets available for writing. You normally leave this callback untouched. It simply writes as much data as possible to the socket and removes the data from the send buffer.

int disconnected_socket (svz_socket_t)

This gets called whenever the socket is lost for some external reason.

int connected_socket (svz_socket_t)

If some piece of code tries to connect to another host via svz_tcp_connect this connection might be established some time later. This callback gets called when the socket is finally connected.

int kicked_socket (svz_socket_t, int)

We call this whenever the socket gets closed by us. The second argument specifies a reason.

int check_request (svz_socket_t)

This gets called whenever data was read from the socket. Its purpose is to check whether a complete request was read, and if it was, it should be handled and removed from the input buffer.

int handle_request (svz_socket_t, char *, int)

This gets called when the check_request got a valid packet. The request arguments contains the actual packet and the second argument is the length of this packet including the packet delimiter.

int idle_func (svz_socket_t)

This callback gets called from the periodic task scheduler. Whenever idle_counter (see below) is non-zero, it is decremented and idle_func gets called when it drops to zero. idle_func can reset idle_counter to some value and thus can re-schedule itself for a later task.

int idle_counter

Counter for calls to idle_func.

void *data

Miscellaneous field. Listener keeps array of server instances here. This array is NULL terminated. Some servers store server specific information here.

void *cfg

When the final protocol detection has been done cfg should contain a pointer to the actual configuration hash map taken from the server instance object. Using coservers

Coservers are designed to complete blocking tasks. Each coserver runs in its own thread/process. There are several coservers implemented: the dns, reverse dns and ident coserver. You need to implement the callback which gets called when a coserver completed its task. This routine must be a svz_coserver_handle_result_t. The first argument is the actual coserver result which might be NULL if the request could not be fulfilled and the latter two arguments are the arguments you specified yourself when issuing the request. To invoke a coserver you use one of the svz_coserver_* macros. The foo server uses the reverse dns coserver to identify the host name of the remote client.
