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-execute 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-execute 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-execute, 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-execute but it is not worth it as it as no reason to re-execute, 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

Utilities
*********

mds-respawn
===========

mds-respawn is a utility intended to be used in ${XDG_CONFIG_HOME}/mdsinitrc. It will spawn a selected set of servers. If a server it spawns exits with a bad status, mds-respawn will respawn it. mds-respawn supports two options in the command line:

--alarm=SECONDS
     Schedule mds-respawn to die in SECONDS seconds. At most 1 minute.

--interval=SECONDS
     Spawned servers that die twice with SECONDS seconds should stop respawning until the signal SIGUSR2 is send to mds-respawn. At most 1 minute.

Commands for servers to spawn are specified within curly braces. Each of the braces must be alone its its own argument. For example:

     mds-respawn --interval=5       \
       { mds-foo --initial-spawn }  \
       { mds-bar --initial-spawn }  &

will spawn and supervise the servers mds-foo and mds-bar. Both spawned with the argument --initial-spawn. When a server is respawed by mds-respawn, --initial-spawn in its argument list will be replaced by --respawn to let the server know it is being respawned.

A server is considered to exit with a failure status unless it exits with the return value 0 or is terminated by the signal SIGTERM.

mds-reg
=======

mds-reg is a utility that can be used to list, available protocols provided by running servers. It can also wait for a set of protocols to become available. To list all available protocols run mds-reg --list. And to wait for the protocol foo run mds-reg --wait=foo. To also wait for the protocol bar run mds-reg --wait=foo,bar or mds-reg --wait=foo --wait=bar. Both of these styles can be mixed if you want to wait for even more protocols.

mds-clip
========

mds-clip is a utility that can be used to review the clipboards on the display and manipulate them. mds-clip recognises the following options:

--push
     Push non-option arguments from the command line into the clipboard.

--expire=SECONDS
     Can be used with --push. The clip will not removed after SECONDS seconds.

--pop
     Pop items from the clipboard whose indices are listed in the command line as non-option arguments. The first index is 1.

--clear
     Pop all items in the clipboard.

--list
     List items in the clipboard whose indices are listed in the command line as non-option arguments. The first index is 1. If no indicies are specified, all clips will be listed.

--size
     Print the size of the clipboard, the number of clips in the clipboard.

--capacity
     Print the capacity of the clipboard, the number of clips the clipboard can hold. If both --size and --capacity is used, the size will be printed on the first line and the capacity will be printed on the second line.

--resize=CAPACITY
     Change the capaciy of the clipboard to CAPACITY clips.

--stdin
     Can be used with --push. If used, the clip that should be placed on the top of the clipboard stack should be read from stdin.

--delimiter=DELIMITER
     Can be used with --stdin or --list. If used with --stdin, an line containing only DELIMITER will delimit two values that should be placed in the clipboard. If used with --list, a line containing only DELIMITER will delimit two values in the output. The default delimiter for --list is an empty line.

-1
     Use the primary clipboard, that is, the text copy clipboard. This is the default clipboard.

-2
     Use the secondary clipboard, that is, the text selection clipboard.

-3
     Use the secondary clipboard, that is, the non-text copy clipboard.

External Utilities
==================

Servers let you use the option --on-init-fork to put the process in the background when it has been initialised. This can used to spawn that depend on each other in linear order. For example, if mds-bar requires that mds-foo is initialised before it can be initialised, you can in ${XDG_CONFIG_HOME}/mdsinitrc write:

     mds-foo --on-init-fork
     mds-bar &

This will start mds-bar when mds-foo has been initialised. However if one of them crashes, that server will not respawn; to fix this mds-respawn can be used, but use of mds-respawn hinders the use of --on-init-fork. Instead you can use --on-init-sh and global semaphores. The packages, and commands, cmdipc and ipcmd can be used for this purpose. We will use cmdipc in an example:

     S=$(cmdipc -Scx set 1 | cut -d ' ' -f 2)
     # Create a System V semaphore with the value 1.

     mds-respawn { mds-foo --on-init-sh="cmdipc -Sk $S p" } &
     # Spawn `mds-foo` and decrease the semaphore with 1 when initialised.

     cmdipc -Sk $S z            # Wait for the semaphore's value to become 0.
     cmdipc -Srk $S             # Remove the semaphore.
     mds-respawn { mds-bar } &  # Spawn `mds-bar`.

This is however seldom necessary as mds-reg can often be used instead, with more abstraction as you would only need to specify what servers need to wait for, not what they provide.

Another useful command (and package) is setpgrp. mds puts itself an all its children in a new process group. However you may want to put processes that are not mds servers or mds utilities in a separate process group. setpgrp can be used to starta process in a new process group.

Servers
*******

An mds display server instance is comprised of multiple small servers that each implements a small part of the display server's functionallity. This chapter will include all servers but the master sever, mds-server and the kernel, mds, the latter of which is not actually a server.

mds-echo
========

mds-echo is a server that echos message that contain the header--value pair Command: echo. This server can be used for debugging and testing as well as to enable network heartbeats.

mds-registry
============

mds-registry is a server that keeps a registry of all protocols that are supported they the sum of all active servers. It can also be used by other servers to wait until a protocol has become available.

mds-vt
======

mds-vt is the server that acquires a virtual terminal for the display, manages virtual terminal switches and enables other servers to get access to the virtual terminal's TTY and informs them of which virtual terminal the display is located on. It also enables other servers to switch the virtual terminals mode to graphical mode or text mode.

mds-clipboard
=============

mds has three clipboards, one for copied text, one for selected text, and one for non-textual data. Each of these clipboards are stacks, just like in GNU Emacs. mds-clipboard implements these clipboards and automatic removal of outdated clips. Clips can be configured to expire based on time or when its originator closes.

mds-iclipboard
==============

mds-iclipboard is an alternative to mds-clipboard that can be used to share clipboard between multiple display servers instances.

mds-drag
========

mds-drag is the server that implements drag-and-drop support.

mds-idrag
=========

mds-idrag is an alternative to mds-drag that lets you drag and drop between display server instances.

mds-kkbd
========

mds-kkbd implements access to the kernel-based keyboard. It does not however implement delay and rate configurations for the kernel-based keyboard as that requires root privileges. The kernel-based keyboard is a keyboard that can be accessed by reconfiguring stdin in a TTY using ioctl and then read from stdin. mds-kkbd does not implement any keyboard layout, rather it broadcasts scancodes and keycode. However it can remap keycodes, but not scancodes.

mds-kkbdrate
============

mds-kkbdrate is a complemental server to mds-kkbd, it implements rate and delay control for the kernel-based keyboard.

mds-kbd
=======

mds-kbd is an alternative to mds-kkbd and mds-kkbdrate. In contrast to mds-kkbd, mds-kbd implements control over individual keybroads rather than utilising the kernels keyboard drivers to treats all keyboards a one keyboard. This server is only useful for multiseat sessions and if you otherwise actually want to handle the keyboards individually.

mds-keytrans
============

mds-keytrans is the server than translates keycodes from mds-kkbd and mds-kbd, and third-party alternatives, to characters and other attributes. It implements the keyboard's layouts including modifiers, letters, other symbols, dead keys and compose.

mds-keystick
============

mds-keystick is a server that can be used to enable sticky keys.

mds-kbdbind
===========

mds-kbdbind is a server similar to xbindkeys. It can be used to run commands upon selected key combinations, for example starting dmenu or change keyboard layout. mds-kbdbind can distinguish keyboards from eachother.

mds-multikey
============

mds-multikey is a server that can bind a key, key combination, or sequence their of to a sequence of keys or key combinations. For example, you could bind `<super>x, y' to simulate that a key `Faux1' is pressed, a key that does not exist, this key press could be picked up by mds-kbdbind to enable mds-kbdbind to respond to squences rather than single keys and single key combinations. alternatively you could bind `<super>x' to press `x' a selected number of times with a short selectable delay between them; or `<super>x, 5' to press `x' five times.

mds-rat
=======

mds-rat is a server that implements support of rat (also known as mouse) devices.

mds-ratbind
===========

mds-ratbind is a server similar to mds-kbdbind. However, mds-ratbind respons to rat and rat cursor actions rather than keyboard actions. It can for example be used to implement hotcorners.

mds-gestures
============

mds-gestures is a server similar to mds-ratbind. However it is specialised to respond to gestures.

mds-kbd2rat
===========

If you do not have a rat or rather use your keyboard, the server mds-kbd2rat can be used to bind keyboard actions to simulate rat actions. This server is a specialisation of mds-kbdbind, rather than spawning generic commands it broadcasts messages without the display server to move the rat cursor and click on rat buttons. mds-kbdbind could be used to do this, but mds-kbd2rat will not spawn a new process for each action.

mds-hwcursor
============

mds-hwcursor is a server that draws the rat cursor to the monitor on a plane separate from all other content on the display. Is less esoteric terms, it implements a hardware cursor.

mds-swcursor
============

mds-swcursor is a server that draws the rat cursor to the monitor on the same plane as all other content on the display. Is less esoteric terms, it implements a software cursor.

mds-cursorshadow
================

mds-cursorshadow is a server that can be used to decorate the rat cursor with a configurable shadow.

mds-cursorgamma
===============

mds-cursorgamma is a server you can use if you use mds-hwcursor to, if not done by the graphics driver, correct the gamma correction on the hardware cursor using software gamma ramps. This of courses works whether you are using hardware or software gamma ramps for your monitor's gamma correction. If can even be used if you do not use gamma correction, in such case, only the cursor will have its gamma corrected.

mds-hwgamma
===========

To enable gamma correction, use the server mds-hwgamma. It implements hardware gamma ramps, that is, gamma ramps assisted by hardware acceleration.

mds-swgamma
===========

If your graphics driver does not support mds-hwgamma, you can instead use mds-swgamma. It implements software gamma ramps, that is, it will modify each pixel according to the selected gamma correction before it is send to the presentation sever. To accelerate this process, mds-swgamma can tell programs how to modify its colours before sending it; the programs can then tell mds-swgamma not to apply its correction. Programs such as video players can also use this to tell the server not to apply gamma correction as that may cause the video to be played back to slowly.

mds-coopgamma
=============

mds-coopgamma is a server that can be used to enable multiple clients to manipulate the gamma ramps without stepping on eachothers toes. It does this by letting clients tell which priority their corrections has and use this data to chain together there modifications. For example if one program wants to apply a red filter to the display and another program wants to correct the monitors' gamma, the red filter program will send lookup tables for the gamma with high priority and the correction program will send its lookup tables with low priority. mds-coopgamma will then apply the latter lookup tables on top of the red filter. The clients can tell mds-coopgamma whether it should remove their changes when they close, or even keep them and wait for the client to restart.

mds-dcvs
========

mds-dcvs is a server than can be used to simulate defective colour vision. That is, it can for example turn the display greyscale (colour blindness) or add a filter the simulates deuteranopia or deuteranomaly. This server is intended for testing that interfaces are suitable for people with defective colour vision.

mds-colour
==========

mds-colour is a server that implements colour names, such as system colours and generic names, for example `red', whose exact colour can be configured by the user. A terminal written for mds whould look up colours such as `red' and `light red' and get the colours the terminal should use by default. Nothing is to be assumed for such colours, not even that `light red' is in fact lighter than `dark red', or that `red' is in fact `red', only that it is the colour the user wants to see when a colour is supposed to be `red'. mds-colour will notify clients when a colour has been reconfigured, added or removed.

mds-colour is also responsible for informing clients on which two colours clients should use and how to dither them (by percent, not by pattern). This is useful if only 16-bit colours can be used, or if only 24-colour can used but gradients between for example sRGB(255, 255, 255) and sRGB(254, 254, 254) is to be drawn.

mds-cool-old-crt
================

mds-cool-old-crt is a server that applies filters used in the terminal emulator cool-old-term to the whole display.

mds-state
=========

mds-state is the server that keeps tracks of the windows' state.

mds-focus
=========

mds-focus is the server focuses windows and windows' components.

mds-kill
========

mds-kill is a server that can be used to send signals to processes by identifying them by their windows. This server can also be used to simply identify the process that owns a window.

mds-screensaver
===============

mds-screensaver is a server that can be used to start a screensaver or deactive monitors when the input devices has not be used for a period of time provided that no client has disabled this. It is capable of deactiving single monitors or start a screensaver on single monitors rather than all monitors.

mds-compositor
==============

mds-compositor is the server that composes the output. It takes output of all windows and arranges it to one image per monitor and sends it to the presentation servers, such as mds-dri and mds-fb. It is also capable of some primitive transformations such as rotation and skewing.

mds-crtc
========

mds-crtc is the server that identifies CRTC:s and provide access to them.

mds-dri
=======

mds-dri is a server that displays content using the Direct Rendering Infrastructure.

mds-fb
======

mds-fd is a server that displays content using frame buffers.

mds-seat
========

mds-seat is a server that enables seat-sandboxing. It can be used to place two users on the same machine without them interfering with each others monitors and input devices. Servers started below mds-seat become shared and servers started above mds-seat become seat-private. mds-seat can filter messages from shared servers so only the appropriate seat receives them.

mds-host
========

mds-host is a server that enables servers like mds-remote running on remote machines to connect to the local machine and its display server.

mds-remote
==========

mds-remote is a server that enables you to connect extend a remote mds with your local machine. This can be used to make a display server instance span multiple computers including its monitors and input devices.

mds-xmds
========

mds-xmds is a server that translates X.org calls to mds calls. It can be used to run X.org-only programs inside mds.

mds-wmds
========

mds-wmds is a server that translates Wayland calls to mds calls. It can be used to run Wayland-only programs inside mds.

mds-mmds
========

mds-mmds is a server that translates Mir calls to mds calls. It can be used to run Mir-only programs inside mds.

mds-mdsx
========

mds-mdsx is a server that translates mds calls to X.org calls. It can be used to enable mds specific programs to run inside the X.org display servers.

mds-mdsw
========

mds-mdsw is a server that translates mds calls to Wayland calls. It can be used to enable mds specific programs to run inside the Wayland display servers.

mds-mdsm
========

mds-mdsm is a server that translates mds calls to Mir calls. It can be used to enable mds specific programs to run inside the display server Mir.

mds-decorator
=============

mds-decorator is a server that provides a simple, reference implementation of a, window decorator.

mds-tile
========

mds-tile is a server that provides a simple, reference implementation of a, tiling window manager.

mds-stack
=========

mds-stack is a server that provides a simple, reference implementation of a, stack window manager.

mds-workspace
=============

mds-workspace is a server that provides simple, reference implementation of, workspaces.

mds-tray
========

mds-tray is a server that provides a simple, reference implementation of a, status icon tray.

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-execute the server. If prepare_reexec failed or has not been called, argv[0] will be used as a fallback. This functions has three input parameters:

     argc
          The number of elements in argv.

     argv
          The command line arguments.

     reexeced
          Whether the server has previously been re-executed

     This function only returns on failure.

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 linear array sentinel doubly 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]);
     }

Linked List
-----------

linked_list_t is a linear array sentinel doubly linked list. This means that is implemented using arrays rather than node references. More specifically, since it is doubly linked (And not using XOR-linking.), it is implemented using three arrays:

values [size_t*]
     The value stored in each node.

next [ssize_t*]
     The next node for each node, edge if the current node is the last node, and LINKED_LIST_UNUSED if there is no node on this position.

previous [ssize_t*]
     The previous node for each node, edge if the current node is the first node, and LINKED_LIST_UNUSED if there is no node on this position.

The linked list has a sentinel node that joins boths ends of the list. The index of this node is stored in the variable edge.

Because the list is implemented using arrays, if the number of elements in it shinks considerably, it will not be able to automatically free unused space. Instead you must call linked_list_pack:

linked_list_pack [(linked_list_t* restrict this) -> int]
     Pack the list so that there are no reusable positions, and reduce the capacity to the smallest capacity that can be used. Note that values (nodes) returned by the list's methods will become invalid. Additionally (to reduce the complexity) the list will be defragment so that the nodes' indices are continuous. This method has linear time complexity and linear memory complexity.

To create a linked list list, allocate a linked_list_t* or otherwise obtain a linked_list_t*, and call linked_list_create with that pointer as the first argument, and the 0 as the second argument, unless you want to tune the initialisation. linked_list_create will return zero on and only on successful initialisation. linked_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.

There are five functions adding and removing items to and from a linked list:

linked_list_insert_after [(this, size_t value, ssize_t predecessor) -> ssize_t]
     Create a new node with the value value and add it to the list *this after the node predecessor. On success, the new node is returned, on failure LINKED_LIST_UNUSED is returned.

linked_list_insert_before [(this, size_t value, ssize_t successor) -> ssize_t]
     Create a new node with the value value and add it to the list *this before the node successor. On success, the new node is returned, on failure LINKED_LIST_UNUSED is returned.

linked_list_remove_after [(this, ssize_t predecessor) -> ssize_t]
     Remove and return the node in the list *this directly after the node predecessor.

linked_list_remove_before [(this, ssize_t successor) -> ssize_t]
     Remove and return the node in the list *this directly before the node predecessor.

linked_list_remove [(this, ssize_t node) -> void]
     Remove the node node from the list *this.

The data type for this is linked_list_t* with the restrict modifier for these and all other linked_list_t functions.

Note that if the node this->edge is removed, the list become circularly linked and the sentinel will become missing which renders invokation of all macros undefined in behaviour. Further note that removing the sentinel while it is the only node in the list invokes undefined behaviour. Also note that addressing non-existing nodes invokes undefined behaviour.

<libmdsserver/linked_list.h> defines two macros for inserting nodes at the edges of a linked list and two macros for removing nodes from the edges of a linked list:

linked_list_insert_beginning [(linked_list_t* this, size_t value) -> ssize_t]
     Create a new node with the value value in insert it to the beginning of the list *this. On success, the new node is returned, on failure LINKED_LIST_UNUSED is returned.

linked_list_insert_end [(linked_list_t* this, size_t value) -> ssize_t]
     Create a new node with the value value in insert it to the end of the list *this. On success, the new node is returned, on failure LINKED_LIST_UNUSED is returned.

linked_list_remove_beginning [(linked_list_t* this) -> ssize_t]
     Remove and return the first node in the list *this.

linked_list_remove_end [(linked_list_t* this) -> ssize_t]
     Remove and return the node node in the list *this.

Additionally the library defines a macro that wrappes the for keyword to iterate over all nodes (except the sentinel node) the a linked list:

foreach_linked_list_node [(linked_list_t this, ssize_t node)]
     Wrapper for `for` keyword that iterates over each element in the list this, and store the current node to the variable named by the parameter node for each iterations.

          void print_linked_list_values(linked_list_t* list)
          {
            ssize_t node;
            foreach_linked_list_node (*list, node)
              printf("%zi\n", list->values[node]);
          }

     Note that the data type for this in the macro is not a pointer.

There is also a function intended for debugging:

linked_list_dump [(linked_list_t* restrict this, FILE* restrict output) -> void]
     The all internal data of the list *this into the stream output.

Tables
------

libmdsserver defines two similar data structures: fd_table_t and hash_table_t. Whenever a function exists for both data structures we will write X_table instead of fd_table and hash_table. Additionally, unless otherwise stated, a function's parameter named this will be of the type hash_table_t* if the function's name start with hash_table and fd_table_t* if the function's name start with fd_table, with the restrict modifier.

X_table_create [(this) -> int]
     Initialises *this so it can be used as a table. Returns zero on and only on success.

     These functions are defined as macros.

X_table_create_tuned [(this, size_t initial_capacity) -> int]
     Initialises *this so it can be used as a table, and makes its initial capacity at least initial_capacity. Returns zero on and only on success.

     hash_table_create_tuned is defined as a macro.

hash_table_create_tuned [(this, size_t initial_capacity, float load_factor) -> int]
     Initialises *this so it can be used as a table, and makes its initial capacity at least initial_capacity and its load factor load_factor. Returns zero on and only on success.

X_table_destroy [(this, free_func* key_freer, free_func* value_freer) -> void]
     Release all resources in the table *this, but do not free this itself. Should be called even if construction fails. If keys_freer is not NULL, this function will be called for each key. If values_freer is not NULL, this function will be called for each value.

X_table_contains_value [(const this, size_t value) -> int]
     Check whether the value value is stored in the table *this.

X_table_contains_key [(const this, key) -> int]
     Check whether the key code is used in the table *this.

     The data type for the parameter key is size_t for hash_table and int for fd_table.

X_table_get [(const this, key) -> size_t]
     Look up a value by its key key in the table *this. Zero will be returned if the key was not used.

hash_table_get_entry [(const this, size_t key) -> hash_entry_t*]
     Look up an entry by its key key in the table *this. NULL will be returned if the key was not used.

X_table_put [(this, key, size_t value) -> size_t]
     Map the value value to the key key in the talbe *this. If a value was already mapped to the key, that value will be returned, otherwise zero will be returned. Zero will also be returned on error. errno will be set to zero on and only on success.

     The data type for the parameter key is size_t for hash_table and int for fd_table.

X_table_remove [(this, key) -> size_t]
     Unmaps the key key for the table *this. If a value was mapped to the key, that value will be returned, otherwise zero will be returned.

     The data type for the parameter key is size_t for hash_table and int for fd_table.

X_table_clear [(this) -> void]
     Unmaps all keys in the table *this.

X_table_unmarshal [(this, char* restrict data, remap_func* remapper) -> int]
     As described in Data Structures but with one additional parameter: remapper. If this parameter is not NULL this function is used to edit values. It will be called once for each value and the output of the function will be used inplace of the input value.

<libmdsserver/hash-table.h> also defines as wrapper macro for the for keyword:

foreach_hash_table_entry [(hash_table_t this, size_t i, hash_entry_t* entry)]
     Iterates over entry element in the hash table *this. On each iteration, the entry will be stored to the variable entry and the bucket index will be stored to the variable i.

          void print_hash_table(hash_table_t* table)
          {
            hash_entry_t* entry;
            size_t i;
            foreach_hash_table_entry (*table, i, entry)
              printf("%zu --> %zu\n", entry->key, entry->value);
          }

     Note the the data type for the parameter this is not a popinter.

The structures hash_table_t and fd_table_t contain the variable value_comparator which by default is NULL. If this variable is set to NULL, two values will be considered equal if and only if they are numerically identical; otherwise two values will be considered equal if and only if value_comparator returned a non-zero value if those two values are used for the function's arguments. The data type for value_comparator is compare_func*.

hash_table_t also contains two other variables:

key_comparator [compare_func*]
     Identical to value_comparator, except it is used for keys rather the values.

hasher [hash_func*]
     By default, the hash value for key is identical to the key itself. However, if this variable is not NULL, it will be used to calculate the hash value for keys.

There is a secondary data structure defined for hash tables: hash_entry_t {also known as struct hash_entry}. It is the data structure used for entries in a hash table. hash_entry_t contain three variables you may be interested in:

key [size_t]
     The key.

value [size_t]
     The value associated with the key.

hash [size_t]
     The hash value of the key.

By inclusion of <libmdsserver/table-common.h>, <libmdsserver/hash-table.h> and <libmdsserver/fd-table.h> defines four typedef:s for function signatures:

compare_func [(size_t a, size_t b) -> int]
     A function that performs a comparison of two objects. Should return non-zero if and only if a and b are to be considered equal in the given context.

hash_func [(size_t value) -> size_t]
     A function that hashes an object or a value. Should return the hash value for value.

free_func [(size_t obj) -> void]
     A function that, to the extent that is appropriate, releases the object obj's resources and free:s it.

remap_func [(size_t obj) -> size_t]
     A function that translates a object into a new object. The function should return new object that should replace the object obj.

If you are working with strings, you may consider including the header file <libmdsserver/hash-help.h>. It defines to useful functions:

string_hash [(const char* str) -> size_t]
     Calculate and returns the hash value of the string str.

string_comparator [(char* str_a, char* str_b) -> int]
     Returns non-zero if either both str_a and str_b are NULL or neither are NULL but are identical strings by content upto their first NUL characters (or by address).

These functions are defined as pure and static inline.

Message Structure
-----------------

Apart from internal data mds_message_t contains four variables:

headers [char**]
     The headers in the message, each element in this list as an unparsed header, it consists of both the header name and its associated value, joined by `: '. A header cannot be NULL (unless its memory allocation failed,) but headers itself is NULL if there are no headers. The `Length' header should be included in this list.

header_count [size_t]
     The number of headers in the message.

payload [char*]
     The payload of the message, NULL if none (of zero-length).

payload_size [size_t]
     The length of the message's payload. This value will be the same as the value of the `Length' header.

There are six functions specific to mds_message_t. The this-parameter's data type for this functions are mds_message_t* with the restrict modifier.

mds_message_initialise [(this) -> int]
     Initialises *this so that it can be used by mds_message_read. Returns zero on and only on success. On failure you should destroy *this using mds_message_destroy.

mds_message_zero_initialise [(this) -> void]
     This function is similar to mds_message_initialise, however it cannot fail and thus have no return value. The difference it is action is that it will not allocate an internal buffer.

mds_message_extend_headers [(this, size_t extent) -> int]
     Ensures that extent additional headers can be stored in the *this. Returns zero on and only on success.

mds_message_read [(this, int fd) -> int]
     Reads the next message from the socket file descriptor fd and stores it in *this. Returns zero on success and non-zero on error or interruption. *this should be destroyed using mds_message_destroy on error but not on interruption. If -2 is returned errno will not have been set; -2 indicates that the message is malformated, which is a state that cannot be recovered from.

mds_message_compose_size [(const this) -> size_t]
     This function is to mds_message_compose as mds_message_marshal_size is to mds_message_marshal.

mds_message_compose [(const this, char* restrict data) -> void]
     This function is similar to mds_message_marshal. The only difference is that it will not store internal data and instead create a message that can be broadcasted in the display server message passing system.

mds-base
********

mds-base.c and mds-base.h as an object filepair whose purpose is similar to libmdsserver. mds-base is compiled into all mds servers and implements common procedures including main. It also complements procedures that are weakly defined, that is, if the server implementation also defines them, the server implementations procedure replaces mds-base's implementation at compile-time.

mds-base defines one function that you can call from threads you create and functions that should be implement depending on specified conditions:

trap_signals [(void) -> int]
     Set up signal traps for all especially handled signals. Returns zero on and only on success.

mds-base weakly defines functions that you can replace if they do not suit your needs:

parse_cmdline [(void) -> int]
     Parses command line arguments. Returns zero on and only on success.

     This function will parse the following options:

     --initial-spawn
          It is the first time the server is spawn by its spawner process.

     --respawn
          The server was respawned.

     --re-exec
          The server is re-executing.

     --alarm=SECONDS
          Kill the process after SECONDS seconds. At most one minute.

     --on-init-fork
          Fork the process to detach it from its parent when the server has been initialised.

     --on-init-sh=COMMAND
          When the server has been initialised, run the command COMMAND.

     --immortal
          The server should to its best not to die. For example do not die if SIGDANGER is received even if that is the server's default action.

connect_to_display [(void) -> int]
     Connects to the display. Returns zero on and only on success.

server_initialised [(void) -> int]
     This function should be called when the server has been properly initialised but before initialisation of anything that is removed at forking is initialised. Returns zero on and only on success.

signal_all [(int signo) -> void]
     This function should be implemented by the actual server implementation if the server is multi-threaded. It sends the singal signo to all threads except the current thread.

received_reexec [(int signo) -> void]
     This function is called when a signal that signals the server to re-execute has been received. The exact received signal is specified by the parameter signo. When this function is invoked, it should set the variables reexecing and terminating to a non-zero value.

received_terminate [(int signo) -> void]
     This function is called when a signal that signals the server to terminate has been received. The exact received signal is specified by the parameter signo. When this function is invoked, it should set the variable terminating to a non-zero value.

fork_cleanup [(int status) -> void]
     This function should be implemented by the actual server implementation if the server has set server_characteristics.fork_for_safety to be a non-zero value. This function is called by the parent server process when the child server process exits, if the server has completed its initialisation. The parameter status specifies the child process exit status as returned by waitpid.

Additionally, mds-base expects the server implementation to define and implement a set of functions:

preinitialise_server [(void) -> int]
     This function will be invoked before initialise_server (if not re-executing) or before unmarshal_server (if not re-executing). Returns zero on and only on success.

initialise_server [(void) -> int]
     This function should initialise the server. It not invoked after a re-execution. Returns zero on and only on success.

postinitialise_server [(void) -> int]
     This function will be invoked after initialise_server (if not re-executing) or after unmarshal_server (if re-executing). Returns zero on and only on success.

marshal_server_size [(void) -> size_t, pure]
     Calculate and returns the number of bytes that will be stored by marshal_server. On failure the server should call abort or exit with failure status by other means. However it should not be possible for this function to fail. marshal_server_size must be pure. (That is, define with and conforming to __attribute__((pure)).).

marshal_server [(char* state_buf) -> int]
     Marshal server implementation specific data into the buffer state_buf. Returns zero on and only on success.

unmarshal_server [(char* state_buf) -> int]
     Unmarshal server implementation specific data from the buffer state_buf and update the servers state accordingly. Returns zero on and only on success.

     On critical failure the program should call abort or exit with failure status by other means. That is, do not let reexec_failure_recover run successfully, if it unrecoverable error has occurred or one severe enough that it is better to simply respawn.

reexec_failure_recover [(void) -> int]
     Attempt to recover from a re-execution failure that has been detected after the server successfully updated it execution image. Returns zero on and only on success.

master_loop [(void) -> int]
     Perform the server's mission. Returns zero on and only on success.

mds-base also defines a number of global variables.

argc [int]
     Number of elements in argv.

argv [char**]
     Command line arguments.

is_respawn [int]
     Whether the server has been respawn rather than this being the initial spawn. This will be at least as true as is_reexec.

is_reexec [int]
     Whether the server is continuing from a self-reexecution.

is_immortal [int]
     Whether the server should do its best to resist event triggered death.

on_init_fork [int]
     Whether to fork the process when the server has been properly initialised.

on_init_sh [char*]
     Command the run (NULL for none) when the server has been properly initialised.

master_thread [pthread_t]
     The thread that runs the master loop.

terminating [volatile sig_atomic_t]
     Whether the server has been signaled to terminate.

reexecing [volatile sig_atomic_t]
     Whether the server has been signaled to re-execute.

socket_fd [int]
     The file descriptor of the socket that is connected to the server.

mds-base expects the server implementation to define a variable that specifies how mds-base should behave:

server_characteristics [server_characteristics_t]
     This variable should declared by the actual server implementation. It must be configured before main is invoked. That is, it should be configured by a constructor. If it is configured at its definition, it is configured by a constructor; that is normally how you want to configured it.

server_characteristics_t {also known as struct server_characteristics} is a packed (That is, define with __attribute__((packed)).) with the following fields:

require_privileges [unsigned : 1]
     Setting this to zero will cause the server to drop privileges as a security precaution.

require_display [unsigned : 1]
     Setting this to non-zero will cause the server to connect to the display.

require_respawn_info [unsigned : 1]
     Setting this to non-zero will cause the server to refuse to start unless either --initial-spawn or --respawn is used.

sanity_check_argc [unsigned : 1]
     Setting this to non-zero will cause the server to refuse to start if there are too many command line arguments.

fork_for_safety [unsigned : 1]
     Setting this to non-zero will cause the server to place itself in a fork of itself when initialised. This can be used to let the server clean up fatal stuff after itself if it crashes. When the child exits, no matter how it exits, the parent will call fork_cleanup and then die it the same manner as the child.

danger_is_deadly [unsigned : 1]
     Setting this to non-zero without setting a signal action for SIGDANGER will cause the server to die if SIGDANGER is received. It is safe to set both danger_is_deadly and fork_for_safety to non-zero, during the call of server_initialised the signal handler for SIGDANGER in the parent process will be set to SIG_IGN independently of the value of danger_is_deadly if fork_for_safety is set to non-zero.

     This setting will be treated as set to zero if --immortal is used.

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

     [file fdl.texinfo not included]