# How to define services and service classes Synit [services](../builtin/index.md) are started in response to [`run-service` assertions](../service.md#run-service). These, in turn, are eventually asserted by the service dependency tracker in response to [`require-service` assertions](../service.md#require-service), once any declared dependencies have been started. So to implement a service, respond to `run-service` records mentioning the service's name. There are a number of concepts involved in service definitions: - **Service name**. A unique identifier for a service instance. - **Service implementation**. Code that responds to `run-service` requests for a service instance to start running, implementing the service's ongoing behaviour. - **Service class**. A parameterized collection of services sharing a common parameterized implementation. A service may be an instance of a service *class* (a parameterized family of services) or may be a simple service that is the only instance of its class. Service dependencies can be statically-declared or dynamically-computed. A service's implementation may be [external](../builtin/daemon.md), running as a subprocess managed by `syndicate-server`; internal, backed by code that is part of the `syndicate-server` process itself; or user-defined, implemented via user-supplied code written in the [configuration language](../scripting.md) or as other actor programs connected somehow to the system bus. An external service may involve a long-running process (a "daemon"; what [`s6-rc`][s6-rc] calls a "longrun"), or may involve a short-lived activity that, at startup or shutdown, modifies aspects of overall system state outside the purview of the supervision tree (what [`s6-rc`][s6-rc] calls a "one-shot"). [s6-rc]: https://skarnet.org/software/s6-rc/overview.html ## Service names Every service is identified with its *name*. A service name can be any [Preserves](../../glossary.md#preserves) value. A simple symbol may suffice, but records and dictionaries are often useful in giving *structure* to service names. Here are a few example service names: - ``[^interesting-service-name] - `` - `` - `` - `` The first two invoke service behaviours that are [built-in to `syndicate-server`](../builtin/index.md); the last three are user-defined service names. ## Defining a simple external service As an example of a simple external service, take the `ntpd` daemon. The following assertions placed in the configuration file `/etc/syndicate/services/ntpd.pr` cause `ntpd` to be run as part of the [Synit services layer](../synit-config.md#loading-of-core-and-services-layers). First, we choose the service name: ``. The name is a `daemon` record, marking it as a supervised [external service](../builtin/daemon.md). Having chosen a name, and chosen to use the external service supervision mechanism to run the service, we make our first assertion, which defines the program to be launched: ```preserves ``` Next, we mark the service as depending on the presence of another assertion, ``. This assertion is managed by the [networking core](../synit-config.md#networking-core). ```preserves > ``` These two assertions are, together, the total of the *definition* of the service. However, without a final `require-service` assertion, the service will not be *activated*. By [requiring the service](../service.md#require-service), we connect the service definition into the system dependency tree, enabling actual loading and activation of the service. ```preserves > ``` ## Defining a service class The following stanza (actually part of [the networking core](../synit-config.md#networking-core)) waits for `run-service` assertions matching a *family* of service names, `>`. When it sees one, it computes the specification for the corresponding command-line, on the fly, substituting the value of the *ifname* binding in the correct places (once in the service name and once in the command-line specification). ```preserves ? >> [ ["udhcpc" "-i" $ifname "-fR" "-s" "/usr/lib/synit/udhcpc.script"] > ] ``` This suffices to define the service. To instantiate it, we may either manually provide assertions mentioning the interfaces we care about, ```preserves >> >> ``` or, as actually implemented in the networking core (in `network.pr` lines [13–15](https://git.syndicate-lang.org/synit/synit/src/commit/a43247fc96e906fbfdeb6e9a6e6d6e9a2f35f9e8/packaging/packages/synit-config/files/etc/syndicate/services/network.pr#L13-L15) and [42–47](https://git.syndicate-lang.org/synit/synit/src/commit/a43247fc96e906fbfdeb6e9a6e6d6e9a2f35f9e8/packaging/packages/synit-config/files/etc/syndicate/services/network.pr#L42-L47)), we may respond to assertions placed in the dataspace by [a daemon, `interface-monitor`](https://git.syndicate-lang.org/synit/synit/src/commit/e9bf91e9de0d2724df15835fc6ea33cbdf23b564/packaging/packages/synit-config/files/usr/lib/synit/python/synit/daemon/interface_monitor.py), whose role is to reflect [AF_NETLINK](https://en.wikipedia.org/wiki/Netlink) events into assertions: ```preserves ? > [ >> ] ``` Here, when an assertion of the form `>` appears in the dataspace, we react by asserting a `require-service` record that in turn eventually triggers assertion of a matching `run-service`, which then in turn results in invocation of the `udhcpc` command-line we specified above. ## Defining non-`daemon` services; reacting to user settings Only service names of the form `` are backed by [external service supervisor](../builtin/daemon.md) code. Other service name schemes have other implementations. In particualr, user-defined service name schemes are possible and useful. For example, in the [configuration relating to setup of mobile data interfaces](../synit-config.md#wifi--mobile-data), service names of the form `` are defined: ```preserves ? > [ ? > [ ? > [ >> $log ! ] ] ] ``` Reading this inside-out, - `run-service` for `qmi-wwan` service names is defined to require a `>` service, defined elsewhere; in addition, when a `run-service` assertion appears, a log message is produced. - the stanza reacting to `run-service` is only active when some `>` assertion exists. - the stanza querying the `mobile-data-apn` user setting is itself only active when `>` has been asserted. In sum, this means that even if a `qmi-wwan` service is requested and activated, nothing will happen until the user enables mobile data and selects an [APN](https://en.wikipedia.org/wiki/Access_Point_Name). If the user later disables mobile data, the `qmi-wwan` implementation will automatically be retracted, and the corresponding `qmi-wwan-manager` service terminated. --- [^interesting-service-name]: This first service name example is interesting because it includes an [embedded capability reference](../../glossary.md#reference) using the `$.` syntax from the [scripting language](../scripting.md) to denote the active scripting language environment dictionary.