mds -- The micro-display server
*******************************

Copyright © 2014 Mattias Andrée

     Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.

Overview
********

mds (mds stands for micro-display server) is a display server protocol and an implementation of said protocol. What makes mds stand out is its core design choice: it is desigend just like a microkernel. Rather than one, possibly modular, process --- a monolithic process --- mds is comprised of many small servers, each exchangable and responsible for one thing.

mds goal is neither security, performance nor a perfect graphical experience. mds is all about flexibility and freedom 0 (The freedom to run the program as you wish, for any purpose).

The reason for having a display server architectured as a microkernel is so that components can be added, remove and replaced online. Additionally, the message passing between the servers makes it easy to design a system that lets you make clients that can listen on messages between the servers and perhaps modify them. This enables you to do so much more with your display server. Moreover, if a single part of the system crashes it does not bring down the whole system, and the crashed server can be respawned with minor side effects. mds is architectured in three layers: a microkernel, a master server and a collection of servers. And clients are actually located on the same layer as the servers, because there is no actual difference, the only thing that separates a server from a client is for what purpose you run it. mds's kernel is a minimal program that do initialisation of the display, such as giving it an index and create runtime files and directories for servers and other programs to use. Then the kernel creates a domain socket for the master server and spawns the master server and respawns it if it crashes. Because of this, if the master server crashes it will not lose its socket when it is respawned. The master server than, on its initial spawn, starts the all servers and other programs that the user have choosen and then starts accepting connections to it and coordinates messages between servers and clients. Further, separating all components into separate processes enables us to only give the servers the privileges they actually need, rather than having one program with root privileges that takes care of everything even things that do not do require any privileges.

All mds's servers, that is all running parts of mds except the kernel, are designed so that they can re-exec themself so that they can be updated online without any side effects. Servers serialises their state, saves it to RAM (in a directory created by the kernel), re-exec themself and loads their serialised state. The kernel cannot do this because when it has spawned the master server it has no reason to re-exec, its only mission is to respawn the master server it if would happen to crash. It would technically be possible to enable the kernel to re-exec but it is not worth it as it as no reason to re-exec, and doing so puts the display server at risk of crashing.

Architecture
************

Layers
======

The mds display server in architectured in three layers. The first layer is called the kernel. The kernel is responsible for acquiring a display server index (As with any display server, the system can have multiple instances of mds running at the same time.), set up environment variables to indicate which display server and display server instance is being used, create a domain socket for the display server and start the master server and restart it if it crashes, and then clean up the system when the display server closes. The kernel only responsible for creating the domain socket for communication with the display server, it is not responsible for using it, that mission falls to the master server.

The second layer is the master server. The master server has two responsibilities: coordinating message passing between other servers and clients (In mds their is no functional distinction between servers and clients, the distinction is purely semantic.) and starting other servers.

The third layer is the other servers and clients. protocolwise there is no specification on how they are started. But in the reference implementation of the master server, this is done by starting a shell script with the pathname ${XDG_CONFIG_HOME}/mdsinitrc and the user is responsible for providing the logic in that shell script. (Moonstruck users are allowed to implement this in C or any other language of their choosing.) These servers implements the actual functionality of the display server.

Interprocess Communication
==========================

Intrinsic to mds is a powerful interprocess communication mechanism. Servers and clients connect to the display server by connecting to a domain socket served by the master server. A server or client that has connected to the display server can do three things:

  * Request assignment of a unique ID.

  * Multicast a message.

  * Join or leave a multicast groups.

Upon assignment of an ID the master server will automatically place the client in a multicast group for that specific client. This automatically multicast group assignment is done by the master server simply so you as a debugger do not forget to do so. When a client is disconnected it will and out a message to a specific multicast group that the client, refered to by it's ID, have closed.

A message in the mds protocol is comprised of two parts: headers and a payload. When a client joins a multicast group it is actually say that it is interested and receiving broadcasts containing a specific header or a specific header--value pair, or that it is interesting in all messages (This could be used for logging, possibly spying and networking.). Thus a message is automatically multicasted to groups indicated by its headers.

The multicast groups and receiving of groups is called interceptions. The interesting property of interceptions is that they may be modifying. When a server registers for message interception it can say that it wants to be able to modify messages. If this is done and the server receives a message for which it has said it want to be able to modify it, the master server will wait for that server to respond before it send the message to the next server in the interception list. The server can choose to do three things with a message that it has opted in for modification of: leave the message as-is, modify the message, or consume the message. A message consumption is done by modify the message to make it empty. A consumed message will not be send to any further clients or servers in the interception list.

To make this mechanism sensible, a server or client can set a priority when it registers for interception (does not need to be modifying.) When a message is broadcasted it will be received by all servers in the interception except the original sender, unless it gets consumes. The order in which the master server sends the message to the recipients is determined by priority the servers registed with. The message first sent to the recipients with highest priority and last to the recipients with lowestr priority, and orderd by the priority between those priorities. Of two or more servers have the same priority the order in which they will receive the message, of those recipients, is arbitrary.

An interesting property of this machanism is demonstrated in the mds-vt server. Unlike most servers mds-vt maintains two concurrent connections to the display. Once mds-vt receives a signal from the OS kernel requesting to switch virtual terminal, mds-vt will from one of its connections send out a message and wait for it to be received in its other connection and the let the OS kernel switch virtual terminal. The secondary connection to the display has registered interception with lower priority of the message that the primary connection broadcasts. This message will be received by other servers that will let the message continue to the next server in the interception list once that server is ready for the OS kernel to switch virtual terminal. All of these server has registered modifying interception of the message but none will actually modify or consume the message; it is only used a mechanism for letting mds-vt know when all servers are ready for the switch without having to know how many they are and wait for a reply from all of them.

Protocol
********

Environment Variables
=====================

A crucial of any display server is letting child processes know which display server they should connect to. X.org does by setting the environment variable DISPLAY to <host>:<display index>, where <host> is empty if the display is one the local machine. In this tradition mds does the same thing with the environment variable MDS_DISPLAY.

mds also creates a new process group and export the new process group ID to the environment variable MDS_PGROUP. This process group can be used to send signals to all mds servers collectively.

Signals
=======

mds servers can re-execute into an updated version of their binary. This can be used to update display server online after a new version has been installed. To do this send the signal SIGUSR1 to the server you want update. If a server does not support online updating it will ignore this signal. If the operating system defines a signal named SIGUPDATE, this signal is used instead of SIGUSR1.

If you need servers to free up allocated memory that they do not use, send the signal SIGDANGER, or if not defined SIGRTMAX. Unimportant servers may choose to die on SIGDANGER.

Filesystem
==========

The mds kernel creates two directories for the mds servers to use: one for runtime data and one for temporary data. These directories are named by MDS_RUNTIME_ROOT_DIRECTORY and MDS_STORAGE_ROOT_DIRECTORY, respectively, by the header file <libmdsserver/config.h>. If the systems runtime data directory is /run and transient temporary data directory is /tmp, and the package name of mds is mds, these directories will be /run/mds and /tmp/.{system-directory}.mds, respectively. In /tmp/.{system-directory}.mds the kernel will create a directory for the display server instance named .data prefixed by the display server index. For example if the display server index is zero, temporary data may be stored in /tmp/.{system-directory}.mds/0.data

As defined by SHM_PATH_PATTERN by <libmdsserver/config.h>, when a server re-executes itself it will marshal its state to the POSIX shared memory unit named /.proc-pid-%ji, where %ji (%ji is the pattern in *printf functions for the data type intmax_t.) is replaced with the process ID of the server. This file will be bound to the pathname /dev/shm/.proc-pid-%ji if POSIX shared memory is stored in /dev/shm by the operating system.

In MDS_RUNTIME_ROOT_DIRECTORY the kernel will create two files. .pid and .socket, both prefixed with the display server index (0.pid and 0.socket if the display server index is 0.). The .pid file contains the process ID of the display server and is used by the kernel to figure out whether an display server index is still in use or just not properly cleaned up. Of course it can be used by any program to find the process ID of the kernel process of a display server instance. The .socket is the domain socket used for communication with the display server and its servers and clients.

Message Passing
===============

Message passing over domain sockets is the underlaying technique for communicating with the display server. To communicate with the display server in the local machine a process must connect to the domain socket created by the display server kernel as named in Filesystem.

Clients should request a unique ID when it connects to the display server. (There is seldom a reason for servers to do this.) To do this the client sends

     Command: assign-id\n
     Message ID: 0\n
     \n

where \n is an LF-line break. The value on the Message ID line does not need to be 0, but servers and clients often start with 0 and count upwards. The value is however bound to an unsigned 32-bit integer. All message must contain this Message ID header, otherwise the message is considered corrupt and is ignored.

The empty line signifies the end of the header list, and in this case the end of the message. But a message may contain payload beneath this empty line. To include a payload, add the header Length that says how many bytes the payload is comprised of.

A header must contain a header name and header value without any trailing or leading spaces, and `: ' (colon, one regular blank space) exactly delimits the name and the value.

When the master server receives this Command: assign-id message it will assign the client a unique ID and send it to the client. (The master server is the only server than can address the client uniquely before it has an ID, so this part can only be implement in the master server.) If the client already has an ID, it will send back that ID to the client. This response consists of two headers ID assignment and In response to, containing the client's new (or possibly already assigned) ID and the value that was in the Message ID header, respectively. For example:

     ID assignment: 0:1\n
     In response to: 0\n
     \n

Notice that the master server never includes Message ID in message originating from it.

As seen in this example, the client ID consists of two integers delimited by a colon (`:'). Both of these integers are unsigned 32-bit integers. This is done this way because unsigned 64-bit integers are forbidden because it is not supportable natively be some programming languages.

Before a has gotten a unique client ID assigned to it, it will be `0:0'.

If a client gets disconnected from the master server, the master server will sends out a signal header message. This header will be Client closed and contain ID of the client that closed. For example:

     Client closed: 0:1\n
     \n

Be aware that if a server or client closes and does not have a unique client ID, this message will be:

     Client closed: 0:0\n
     \n

Once a client has an unique client ID assigned to it, it should always include the header Client ID in its messages. The value of Client ID should be the client's ID. If a server wants to address this client, it should include the header To with the value set to the recipient's client ID. Be aware that such message may not be sent to that recipient uniquely, any server or client is free to sign up for receive of such message, any messages or message contain any other header or header--value pair that may also be included in the header.

Interception
============

As discussed in Interprocess Communication, interception in the primary feature of mds's message passing system. Not only does it enable servers to select which message it wants to receive in order to provide it's service. It also enables clients to do anything, things that was never anticipated. As an exaple of its power, mds does not provide any protocol for taking screenshots or recording a session. Instead, a screenshot application signs up for messages pass between the compositor and presentation servers, and simply requests that the compositor resends the screen, a feature intended for the presentation servers. A screen recoding application would do the same and just hang on and record all message passed between the servers.

If you want your server or client to receive all messages passed around in the display server, simply sign up for all messages:

     Command: intercept\n
     Message ID: 0\n
     \n

But if you only want messages contain the header Command, include that header in the payload of the message:

     Command: intercept\n
     Message ID: 0\n
     Length: 8\n
     \n
     Command\n

It is allowed to include multiple headers. You can also be more strict, and require a specific value for a header, for example:

     Command: intercept\n
     Message ID: 0\n
     Length: 16\n
     \n
     Command: get-vt\n

You may mix these two types of requirements freely. Your client will receive any message that satisfies at least one of the requirements, these requirements may be split into multiple message or coalesced into one message; but you cannot request to include receive a message if multiple requirements are satisfied.

Alternatively you can choose to stop receiving message that satisfies requirements. For example:

     Command: intercept\n
     Stop: yes\n
     Message ID: 1\n
     Length: 16\n
     \n
     Command: get-vt\n

Or stop receiving all messages:

     Command: intercept\n
     Stop: yes\n
     Message ID: 1\n
     \n

Note that this will stop you from receiving messages contain the To header addressed to you until you request to receiving such messages again.

When you sign up for message you may request to be able to modify them before that are send to the next client in the list of client that should receive them. To do this include the header--value pair Modifying: yes:

     Command: intercept\n
     Modifying: yes\n
     Message ID: 0\n
     Length: 30\n
     \n
     Command: keyboard-enumeration\n

It is up to the client to keep track of which message that it may modify. When you receive a message that you can modify you must respond when you are done with the message.

For example, if you have signed up for Command: keyboard-enumeration with the ability to modify such messages and the message

     Command: keyboard-enumeration\n
     To: 0:1\n
     In response to: 2\n
     Message ID: 1\n
     Length: 7\n
     \n
     kernel\n

is send from a server, you may receive it as

     Command: keyboard-enumeration\n
     To: 0:1\n
     In response to: 2\n
     Message ID: 1\n
     Length: 7\n
     Modify ID: 4\n
     \n
     kernel\n

Be aware that the Modify ID may be included even if you have not signed up to be able to modify the message, it is enough that one client before you has or it was originally included (You may however not include this header when you send out an orginal message).

If you receive the message as such and want to add the line on-screen-keyboard-20376 to the payload should send out: (The first line containing starting with Message ID is an example, it should be whatever is appropriate for your client.)

     Modify ID: 4\n
     Message ID: 2\n
     Modify: yes\n
     Length: 127\n
     \n
     Command: keyboard-enumeration\n
     To: 0:1\n
     In response to: 2\n
     Message ID: 1\n
     Length: 32\n
     Modify ID: 4\n
     \n
     kernel\n
     on-screen-keyboard-20376\n

If you however decide not to modify the message send out

     Modify ID: 4\n
     Message ID: 2\n
     Modify: no\n
     \n

There is also a third option: to consume to the message. This stops any further clients from receiving the message. This is done by modifying the message into an empty message:

     Modify ID: 4\n
     Message ID: 2\n
     Modify: yes\n
     \n

You may choose to include the header--value pair Length: 0, it is however redundant and discouraged.

This mechanism of being able to modify message does not make much sense unless you can control in the order the clients receive messages. This is done with what is called priority. The higher priority you have, the earlier you will receive the message. The default priority is zero, and the priority is bound to a signed 64-bit integer. If you want to be able to list yourself in Command: keyboard-enumeration message, you should sign up with a positive priority since the final recipient or requested the enumeration will receive it with priority zero. Therefore you should sign up for such message with a message like: (4611686018427387904 is halfway to the maximium value.)

     Command: intercept\n
     Modifying: yes\n
     Priority: 4611686018427387904\n
     Message ID: 0\n
     Length: 30\n
     \n
     Command: keyboard-enumeration\n

libmdsserver
************

libmdsserver is library written for the reference implementation of the mds servers. libmdsserver does not contain support or any protocols, rather it contains auxiliary functions, macros, data structures such as linked lists and hash tables, and support the basics of the message passing protocol: receiving message and decode it into headers and payloads.

Macros
======

The header file <libmdsserver/macros.h> contains macros for readability and code reduction, it also contains macros and definitions for portability; they may either provide portability by nature, or provide one place to do modifications to port the system.

xsnprintf [(char buffer[], char* format, ...) -> int]
     This is a wrapper for snprintf that allows you to forget about the buffer size. When you know how long a string can be, you should use sprintf. But when you cannot know for sure you should use xsnprintf. xsnprintf works exactly as sprintf, but it will require that the first argument is defined using [] rather than * because it will use this to find out how large the buffer is so it can call snprintf with that size.

eprint [(const char* format) -> int]
     A wrapper for fprintf that prints a string prefixed with the value value of *argv to stderr. Because eprintf naïvely wraps fprintf, all `%':s in the string must be duplicated.

eprintf [(const char* format, ...) -> int]
     eprint extends eprint with variadic arguments that can be used to insert values into the format string just like you can do in fprintf.

with_mutex [(pthread_mutex_t mutex, instructions)]
     Wraps instructions with errno = pthread_mutex_lock(mutex); and errno = pthread_mutex_unlock(mutex);, so a set of instructions can be invoked inside mutex protection.

with_mutex_if [(pthread_mutex_t mutex, condition, instructions)]
     An alternative to with_mutex where instructions is wrapped around if (condition) which in turn is wrapped inside the mutex protection.

max [(a, b)]
     Returns the higher value of a and b.

min [(a, b)]
     Returns the lower value of a and b.

buf_cast [(char* buffer, type, size_t index)]
     Casts buffer to a type buffer and subscripts to the index:th element. You can either use this function as a getter or a setter.

buf_set [(char* buffer, type, size_t index, type variable) -> type]
     Wrapper for buf_cast that sets the addressed element to the value of variable.

buf_get [(const char* buffer, type, size_t index, type variable) -> type]
     Wrapper for buf_cast that sets the value of variable to the value of the addressed element.

buf_next [(char* buffer, type, size_t count) -> char*]
     Increases the pointer buffer by the size of type count types.

buf_prev [(char* buffer, type, size_t count) -> char*]
     Decreases the pointer buffer by the size of type count types.

buf_set_next [(char* buffer, type, type variable) -> type]

          buf_set(buffer, type, 0, variable),
          buf_next(buffer, type, 1);

buf_get_next [(char* buffer, type, type variable) -> type]

          buf_get(buffer, type, 0, variable),
          buf_next(buffer, type, 1);

strequals [(const char* a, const char* b) -> int]
     Evaluates whether the strings a and b are equals, neither may be NULL.

startswith [(const char* haystack, const char* needle) -> int]
     Evaluates whether the string haystack starts with the string needle, neither may be NULL.

drop_privileges [(void) -> int]
     Sets the effective user to the real user and the effective group to the real group. This is used by most servers and ensure that they are not running with unnecessary privileges. Returns zero on and only on success.

monotone [(struct timespec* time_slot) -> int]
     Stores the time of an unspecified monotonic clock into time_slot. Returns zero on and only on success.

close_files [(condition) -> void]
     Closes all file descriptors named by a variable fd for which condition evalutes to non-zero.

xfree [(void** array, size_t elements) -> void]
     Calls free on the first elements elements in array, and than calls free on array. This macro requires size_t i is declared.

xmalloc [(type* var, size_t elements, type) -> int]
     Allocates a type* with elements elements and store the allocated pointer to var. Returns zero on and only on success.

xcalloc [(type* var, size_t elements, type) -> int]
     Allocates a zero-initialised type* with elements elements and store the allocated pointer to var. Returns zero on and only on success.

xrealloc [(type* var, size_t elements, type) -> int]
     Reallocates var and updates the variable var accordingly. var will be allocated to have elements elements of the type type. If var is NULL a new allocation is created. If elements is zero, var will be deallocated. Returns zero on and only on success. On failure, var will be NULL, so you must store the var into another variable in case this macro fails.

growalloc [(type* old, type* var, size_t elements, type) -> int]
     When using this macro var should be a type* pointer allocated for elements elements of the type type. This macro will reallocate var to contain twice as many elements and update elements accordingly. On failure nothing changes. You must specify an auxiliary type* variable and specify it in as the old parameter. Returns zero on and only on success.

xperror [(const char* str) -> void]
     Invokes perror(str) if and only if errno is non-zero and then sets errno to zero. str should unless you have a specific reason be *argv.

fail_if [(condition) -> void]
     If condition is satisfied, a jump is made to the label pfail. pfail: should be used for calling xperror and return -1.

exit_if [(condition, instructions) -> void]
     If condition is satisfied, instructions is invoked and 1 is return:ed.

Additionally, <libmdsserver/macros.h> defines any missing signal name: currenly SIGDANGER and SIGUPDATE, and by inclusion of <libmdsserver/macro-bits.h>, variants of atoi for portability and convenience:

atoz
     Parse a human readable const char* 10-radix integer to a size_t.

atosz
     Parse a human readable const char* 10-radix integer to a ssize_t.

atoh
     Parse a human readable const char* 10-radix integer to a short int.

atouh
     Parse a human readable const char* 10-radix integer to an unsigned short int.

atou
     Parse a human readable const char* 10-radix integer to an unsigned int.

atoul
     Parse a human readable const char* 10-radix integer to an unsigned long int.

atoull
     Parse a human readable const char* 10-radix integer to an unsigned long long int.

ato8
     Parse a human readable const char* 10-radix integer to an int8_t.

atou8
     Parse a human readable const char* 10-radix integer to an uint8_t.

ato16
     Parse a human readable const char* 10-radix integer to an int16_t.

atou16
     Parse a human readable const char* 10-radix integer to an uint16_t.

ato32
     Parse a human readable const char* 10-radix integer to an int32_t.

atou32
     Parse a human readable const char* 10-radix integer to an uint32_t.

ato64
     Parse a human readable const char* 10-radix integer to an int64_t.

atou64
     Parse a human readable const char* 10-radix integer to an uint64_t.

atoj
     Parse a human readable const char* 10-radix integer to an intmax_t.

atouj
     Parse a human readable const char* 10-radix integer to an uintmax_t.

Auxiliary Functions
===================

In the header file <libmdsserver/util.h>, libmdsserver defines common functions to help write servers more concisely.

parse_client_id [(const char* str) -> uint64_t]
     Convert a client ID string into a client ID integer.

getenv_nonempty [(const char* var) -> char*]
     Read an environment variable, return NULL if the variable's value is an empty string.

prepare_reexec [(void) -> int]
     Prepare the server so that it can re-execute into a newer version of the executed file.

     This is required for two reasons:

      1. We cannot use argv[0] as PATH-resolution may cause it to reexec into another pathname, and maybe to wrong program. Additionally argv[0] may not even refer to the program, and chdir could also hinter its use.

      2. The kernel appends ` (deleted)' to /proc/self/exe once it has been removed, so it cannot be replaced.

     The function will should be called immediately, it will store the content of /proc/self/exe. Return zero on success and -1 on error.

reexec_server [(int argc, char** argv, int reexeced) -> void]
     Re-exec the server. This function only returns on failure.

     If `prepare_reexec` failed or has not been called, `argv[0]` will be used as a fallback.

     param  argc      The number of elements in `argv`
     param  argv      The command line arguments
     param  reexeced  Whether the server has previously been re-exec:ed

xsigaction [(int signo, void (*function)(int signo)) -> int]
     sigaction with the same parameters as signal. This function should only be used for common mds signals and signals that does not require any special settings. This function may choose to add additional behaviour depending on the signal, such as blocking other signals. Returns zero on success and -1 on error.

send_message [(int socket, const char* message, size_t length) -> size_t]
     Send the message messsage, of length length over the socket that is access with the file descriptor socket. Returns the number of bytes that have been sent, even on error.

strict_atoi [(const char* str, int* value, int min, int max) -> int]
     A version of atoi that is strict about the syntax and bounds. Parses the string str into an int and stores it in *value. If the string is not a 10-radix integer or has a value outside [min, max], -1 is returned, otherwise zero is returned.

full_write [(int fd, const char* buffer, size_t length) -> int]
     Send the buffer buffer, with the length length, into the file whose file descriptor is fd and ignores interruptions. Returns zero on success and -1 on error.

full_read [(int fd, size_t* length) -> char*]
     Read the file whose file descriptor is fd completely and ignore interruptions. If length if not NULL, the length of the read file is stored in *length. On success, the read content is retured, on error NULL is returned.

startswith_n [(const char*, const char*, size_t, size_t) -> int]
     Check whether a string begins with a specific string, where neither of the strings are necessarily NUL-terminated. The parameters are:

     const char* haystack
          The string that should start with the other string.

     const char* needle
          The string the first string should start with.

     size_t haystack_n
          The length of haystack.

     size_t needle_n
          The length of needle.

     Returns 1 if haystack beings with needle, otherwise zero is returned.

uninterruptable_waitpid [(pid_t pid, int* restrict status, int options) -> pid_t]
     Wrapper around waitpid that never returns on an interruption unless it is interrupted one hundred times within the same clock second. The parameters and return value are exactly those of waitpid.

Data Structures
===============

libmdsserver provides a small set of datastructures that are used by the mds servers. All of these are written with marshal-functionallity.

client_list_t {also known as struct client_list}
     In the header file <libmdsserver/client-list.h>, libmdsserver defines a dynamic list for storing client ID:s.

linked_list_t {also known as struct linked_list}
     In the header file <libmdsserver/linked-list.h>, libmdsserver defines a double linked linear sentinel-array-linked list.

hash_table_t {also known as struct hash_table}
     In the header file <libmdsserver/hash-table.h>, libmdsserver defines a hash table.

fd_table_t {also known as struct fd_table}
     In the header file <libmdsserver/fd-table.h>, libmdsserver defines a lookup table for small positive integer keys, intended as an alternative to hash tables for file descriptors as keys.

mds_message_t {also known as struct mds_message}
     In the header file <libmdsserver/mds-message.h>, libmdsserver defines a data structure for message between the server or client and the master server, with the capability of reading for a socket.

These data structures share a common set of associated function. However, they do not use the same functions; they are identical except they are are named with the associated data structure. We will use X_t as an example.

X_destroy [(X_t* restrict this) -> void]
     Releases all resouces in *this, this itself is however not free:d.

     However, hash_table_destory and fd_table_destory have another signature.

X_clone [(const X_t* restrict this, X_t* restrict out) -> int]
     Create a deep duplicate of *this and store it in *out.

X_marshal_size [(const X_t* restrict this) -> size_t]
     Calculates the exact allocate size needed for the parameter data in the function X_marshal if called with the same this parameter.

X_marshal [(const X_t* restrict this, char* restrict data) -> void]
     Marshal the state of *this into data. The number of bytes that will be stored (contiguously) in data can be calculated with X_marshal_size.

X_unmarshal [(X_t* restrict this, char* restrict data)) -> int]
     Unmarshal a X_t from data into *this. Returns zero on success and -1 on error. The number of bytes read from data should, if required, have been precalculated with X_marshal_size and stored in an earlier location of data.

     However, hash_table_unmarshal and fd_table_unmarshal have another signature.

Client List
-----------

To create a client list, allocate a client_list_t* or otherwise obtain a client_list_t*, and call client_list_create with that pointer as the first argument, and the 0 as the second argument, unless you want to tune the initialisation. client_list_create will return zero on and only on successful initialisation. client_list_create's second parameter --- size_t capacity --- can be used to specify how many element the list should initially fit. It will grow when needed, but it is a good idea to tell it how many elements you are planning to populate it with.

client_list_t has two associated functions for manipulating its content:

client_list_add [(client_list_t* restrict this, uint64_t client) -> int]
     This function will add the element client to the list *this, and return zero on and only on success.

client_list_remove [(client_list_t* restrict this, uint64_t client) -> void]
     This function will remove exactly one occurrence, provided that there is at least on occurrence, of the element client for the list *this.

The retrieve the number elements stored in a list, reads its variable size_t size. The variable uint64_t* clients is used to retrieve stored elements.

     void print_elements(client_list_t* this)
     {
       size_t i;
       for (i = 0; i < this->size; i++)
         printf("Element #%zu: %" PRIu64 "\n", i, this->elements[i]);
     }

GNU Free Documentation License
******************************

     [file fdl.texinfo not included]