This commit is contained in:
Tony Garnock-Jones 2022-05-14 15:12:12 +02:00
parent 38bef75093
commit 7041a1ea76
10 changed files with 262 additions and 12 deletions

View File

@ -21,15 +21,13 @@
- [Configuration watcher](./operation/builtin/config-watcher.md)
- [Daemons and external programs](./operation/builtin/daemon.md)
- [Configuration files and directories](./operation/synit-config.md)
- [Defining services and service classes]()
- [Scheduling tasks]()
- [Timer tolerances]()
- [One-off tasks]()
- [Repeating tasks]()
- [Managing user settings]()
- [Restarting services]()
- [Rebooting and powering off the machine]()
- [Suspending the machine]()
- [How-to ...](./operation/howto/index.md)
- [Define services and service classes](./operation/howto/define-services.md)
- [Restart services](./operation/howto/restart-services.md)
- [Schedule one-off or repeating tasks](./operation/howto/schedule-tasks.md)
- [Manage user settings](./operation/howto/manage-user-settings.md)
- [Reboot and power off the machine](./operation/howto/reboot-and-power-off.md)
- [Suspend the machine](./operation/howto/suspend.md)
- [Standard tools]()
- [preserves-tools]()
- [Standard libraries]()

View File

@ -0,0 +1,176 @@
# 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:
- `<config-watcher "/foo/bar" $.>`[^interesting-service-name]
- `<daemon docker>`
- `<milestone network>`
- `<qmi-wwan "/dev/cdc-wdm0">`
- `<udhcpc "eth0">`
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: `<daemon ntpd>`. 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
<daemon ntpd "ntpd -d -n -p pool.ntp.org">
```
Next, we mark the service as depending on the presence of another assertion, `<default-route
ipv4>`. This assertion is managed by the [networking core](../synit-config.md#networking-core).
```preserves
<depends-on <daemon ntpd> <default-route ipv4>>
```
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
<require-service <daemon ntpd>>
```
## 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, `<daemon <udhcpc `*ifname*`>>`. 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
? <run-service <daemon <udhcpc ?ifname>>> [
<daemon
<udhcpc $ifname>
["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
<require-service <daemon <udhcpc "eth0">>>
<require-service <daemon <udhcpc "wlan0">>>
```
or, as actually implemented in the networking core (in `network.pr` lines
[1315](https://git.syndicate-lang.org/synit/synit/src/commit/a43247fc96e906fbfdeb6e9a6e6d6e9a2f35f9e8/packaging/packages/synit-config/files/etc/syndicate/services/network.pr#L13-L15)
and
[4247](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
? <configure-interface ?ifname <dhcp>> [
<require-service <daemon <udhcpc $ifname>>>
]
```
Here, when an assertion of the form `<configure-interface `*ifname*` <dhcp>>` 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 `<daemon `*name*`>` 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 `<qmi-wwan
`*devicePath*`>` are defined:
```preserves
? <user-setting <mobile-data-enabled>> [
? <user-setting <mobile-data-apn ?apn>> [
? <run-service <qmi-wwan ?dev>> [
<require-service <daemon <qmi-wwan-manager $dev $apn>>>
$log ! <log "-" { line: "starting wwan manager", dev: $dev, apn: $apn }>
]
]
]
```
Reading this inside-out,
- `run-service` for `qmi-wwan` service names is defined to require a `<daemon
<qmi-wwan-manager `*deviceName APN*`>>` 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 `<user-setting
<mobile-data-apn `*APN*`>>` assertion exists.
- the stanza querying the `mobile-data-apn` user setting is itself only active when
`<user-setting <mobile-data-enabled>>` 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.

View File

@ -0,0 +1,3 @@
# How-to ...
The following pages walk through examples of common system administration tasks.

View File

@ -0,0 +1,21 @@
# How to manage user settings
Send a [`user-settings-command`]() message containing an `assert` or `retract` record
containing the setting assertion to add or remove. Use the [`!` operator of the configuration
language](../scripting.md#SendInstruction) to send a message (as opposed to make an assertion):
```preserves
! <user-settings-command <assert <mobile-data-enabled>>>
! <user-settings-command <assert <mobile-data-apn "internet">>>
! <user-settings-command <retract <mobile-data-enabled>>>
```
In future, a command-line tool for sending such messages will be provided; for now, create
temporary configuration language scripts in `/run/etc/syndicate/services`:
```shell
THROCK=/run/etc/syndicate/services/throck.pr
echo '! <user-settings-command <assert <mobile-data-enabled>>>' > $THROCK
sleep 1
rm -f $THROCK
```

View File

@ -0,0 +1,5 @@
# How to reboot and power off the machine
(TODO. Not yet implemented: eventually, `synit-pid1` will respond to messages/assertions from
the dataspace, implementing the necessary coordination for a graceful shutdown procedure. For
now, `sync` three times, sleep a bit, and `reboot -f` or `poweroff -f`...)

View File

@ -0,0 +1,21 @@
# How to restart services
Send a [`restart-service`](../service.md#restart-service) message mentioning the [service
name](./define-services.md#service-names) of the service to restart. Use the [`!` operator of
the configuration language](../scripting.md#SendInstruction) to send a message (as opposed to
make an assertion):
```preserves
! <restart-service <daemon <wifi-daemon "wlan0">>>
! <restart-service <daemon user-settings-daemon>>
```
In future, a command-line tool for sending messages to a system dataspace will be provided; for
now, create temporary configuration language scripts in `/run/etc/syndicate/services`:
```shell
THROCK=/run/etc/syndicate/services/throck.pr
echo '! <restart-service <daemon <wifi-daemon "wlan0">>>' > $THROCK
sleep 1
rm -f $THROCK
```

View File

@ -0,0 +1,21 @@
# How to schedule one-off or repeating tasks
(TODO. Not yet implemented: a cron-like program will eventually respond to assertions demanding
periodic or delayed execution of tasks (likely expressed as assertions, making it more of a
delayed-or-periodic-assertion-producing program).)
## Timer tolerances
Apple has come up with the useful idea of a [timer tolerance][apple-timer-tolerance],
applicable to both repeating and one-off timers. In [their
documentation][apple-timer-tolerance], they write:
> The timer may fire at any time between its scheduled fire date and the scheduled fire date
> plus the tolerance. [...] A general rule, set the tolerance to at least 10% of the interval
> [...] Even a small amount of tolerance has significant positive impact on the power usage of
> your application.
[apple-timer-tolerance]: https://developer.apple.com/documentation/foundation/timer#1667624
## One-off tasks
## Repeating tasks

View File

@ -0,0 +1,5 @@
# How to suspend the machine
(TODO. Not yet implemented: eventually, assertions in the dataspace will control the desired
suspend state, and reactive stanzas will allow responses to any kind of ambient conditions to
include changes in the suspend state.)

View File

@ -127,7 +127,7 @@ active target register.
As a convenient shorthand, the compiler also interprets every Preserves record or dictionary in
*Instruction* position as denoting a *ValueExpr* to be used to produce a value to be asserted.
### Sending a message
### <span id="SendInstruction"></span>Sending a message
*SendInstruction* = `! `*ValueExpr*

View File

@ -67,7 +67,7 @@ dependencies.
The built-in handler for `require-service` assertions will assert `run-service` automatically
once all dependencies have been satisfied.
### Declare a dependency among services
### <span id="depends-on"></span>Declare a dependency among services
```preserves-schema
ServiceDependency = <depends-on @depender any @dependee ServiceState>.
@ -109,7 +109,7 @@ ServiceObject = <service-object @serviceName any @object any>.
A running service publishes zero or more of these. The details of the object vary by service.
### Request a service restart
### <span id="restart-service"></span>Request a service restart
```preserves-schema
RestartService = <restart-service @serviceName any>.