The frontend service file¶
A supervised service is, under the hood, made of several different files whose
relationships can be complex to understand and manage. The frontend service file
of the 66 program lets you describe every kind of service — classic,
oneshot, module and event —
in a single place, and 66 generates everything its native supervision needs from
it.
Quickstart¶
The smallest frontend that actually runs needs just two sections — a [Main] that
names the service type, and a [Start] that says what to run:
[Main]
Type = classic
[Start]
Execute = ( /usr/bin/true )
A realistic frontend fills in a description, a run command, and its environment.
Here is ntpd, annotated:
[Main]
Type = classic
Description = "ntpd daemon"
[Start]
Execute = (
foreground { mkdir -p -m 0755 ${RUNDIR} }
execl-cmdline -s { ntpd ${CMD_ARGS} }
)
[Environment]
RUNDIR=!/run/openntpd
CMD_ARGS=!-d -s
Type = classic makes it a supervised service, restarted if it crashes. In
[Environment], the ! prefix means the variable is used at start time but not
exported into the service's runtime environment (see [Environment]).
Comments must be on their own line — 66 does not strip a # placed after a value.
From here you add sections as you need them: [Stop] for a custom stop sequence,
[Logger] to tune logging, [Execute] for resource limits and capabilities, and
so on. Each is documented below. A full template listing every valid key is in
Appendix D.
Where the files live¶
By default 66 expects to find service files in /usr/share/66/service and
/etc/66/service for the root user, and in /usr/share/66/service/user and
/etc/66/service/user for regular accounts. For regular accounts,
$HOME/.66/service takes priority over the previous ones. These locations
can be changed at compile time by passing the -D system-service-dir=DIR,
-D sysadmin-service-dir=DIR and -D user-service-dir=DIR options to meson setup.
The file name usually corresponds to the name of the daemon and carries no extension or prefix:
/usr/share/66/service/dhcpcd
/usr/share/66/service/very_long_name_which_make_no_sense
Anatomy: the eight sections¶
A frontend is an INI file made of sections, each holding one or more
key = value pairs. The whole file follows one mental model: identity →
start → stop → logging → environment, with two specialised sections for modules
and for the event system.
| Section | Required | Purpose |
|---|---|---|
[Main] |
yes | Identity, dependencies, supervision policy, permissions. |
[Start] |
yes | The command that starts the service, its start timeout, readiness and crash budget. |
[Stop] |
no | A custom stop sequence (defaults to signalling the process). |
[Logger] |
no | Behaviour of the native 66-log logger. |
[Environment] |
no | Environment variables for the service. |
[Regex] |
no | Substitution rules — module services only. |
[Execute] |
no | Resource limits, capabilities, process attributes and I/O redirection. |
[Event] |
no | Turn service into an event reactor. |
[Main] must be declared first. Beyond that, order does not matter.
General parsing rules¶
- Format.
INIwith a specific syntax on the key field. The key name can contain special characters like-(hyphen) or_(low line), except@(commercial at) which is reserved. - No empty values. If a key is set, its value can not be empty.
- Comments occupy their own line, beginning with the number sign
#; a#placed after a value is not a comment (it becomes part of the value). Empty lines are allowed. - Keys are case sensitive and can not be renamed. Most names are specific enough to avoid confusion.
- Section names are written between square brackets
[]and must begin with an uppercase letter followed by lowercase letters only — no special characters, no numbers. - A section can be mandatory without all of its keys being mandatory.
The value of each key is parsed in one of several fixed formats (inline, quotes, brackets, uint, path…). Every key below states which one it uses; the formats themselves are described once in Appendix A — Value syntax reference.
Section [Main]¶
This section is mandatory and must be declared first. Its keys fall into
four groups: identity, dependencies, supervision policy, and permissions & files.
A fifth group — the event source keys — applies only when
Type = event.
Identity¶
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
Type |
inline | yes | — | classic / oneshot / module / event |
Version |
inline | no | installed 66 version |
service version string |
Description |
quotes | no | "<name> service" |
one-line human summary |
Type¶
Type = classic
Defines the service type. Determines how 66 orchestrates startup and supervision.
-
mandatory: yes (!)
-
syntax: inline
-
valid values :
- classic : Standard supervised service. Runs continuously and is automatically restarted if it crashes.
- oneshot : Executes once and does not restart. Suitable for initialization tasks.
- module : Configurable set of different type of service; integrates with the
[Regex]section for file and directory transformations. - event : A non-supervised event source for the event system. It runs no process and has no
[Start]section; its whole configuration lives in[Main]and is selected byEventType. Aclassic/oneshot/moduleservice becomes an event reactor instead by adding an[Event]section — it does not use this value.
Version¶
Version = 0.1.0
Specifies the semantic version of the service. This helps track updates and compatibility. If not specified, defaults to the actual installed version of 66.
-
mandatory: no
-
syntax: inline
-
valid values:
- Any valid version with number, alphabetical, release or mixed components. See Appendix C — The Version key in depth.
Description¶
Description = "ntpd daemon"
Provides a concise, human-readable summary of the service’s purpose. Enclosed in double quotes. If not specified, defaults to "
-
mandatory: no
-
syntax: quote
-
valid values:
- Anything you want.
Dependencies¶
66 resolves the full dependency graph for you — you never list transitive
dependencies by hand (see 66). In every bracketed
list here, a name can be commented out by prefixing it with #, e.g.
Depends = ( fooA #fooB fooC ).
| Key | Syntax | Required | Meaning |
|---|---|---|---|
Depends |
brackets | no | services that must start before this one |
RequiredBy |
brackets | no | services that depend on this one (reverse) |
OptsDepends |
brackets | no | enable the first available of these, or none |
Provide |
brackets | no | aliases this service answers to |
Conflict |
brackets | no | services that can not run/enable alongside this one |
Depends¶
Depends = ( fooA fooB fooC )
Declares the mandatory service dependencies. Each listed service must start successfully before this service launches.
-
mandatory: no
-
syntax: brackets
-
valid values:
- The name of any valid service.
It is unnecessary to manually define chained sets of dependencies, see 66.
A service can be commented out by placing the number sign
#at the beginning of the name like this:Depends = ( fooA #fooB fooC )
RequiredBy¶
RequiredBy = ( fooX fooY )
Specifies reverse dependencies—services that depend on this service. Starting or enabling this service automatically updates those listed.
-
mandatory: no
-
syntax: brackets
-
valid values:
- The name of any valid service.
It is unnecessary to manually define chained sets of dependencies, see 66.
A service can be commented out by placing the number sign
#at the beginning of the name like this:RequiredBy = ( fooX #fooY )
OptsDepends¶
OptsDepends = ( fooA fooB )
Lists optional dependencies. 66 will enable the first available service from this list at startup.
-
mandatory: no
-
syntax: brackets
-
valid values:
- The name of any valid service. A service declared as optional dependencies is not mandatory. The parser will look the corresponding service:
- If enabled, it will warn the user and do nothing.
- If not, it will try to find the corresponding frontend file.
- If the frontend service file is found, it will enable it.
- If it is not found, it will warn the user and do nothing.
The order is important (!). The first service found will be used and the parse process of the field will be stopped. So, you can consider
OptsDependsfield as: "enable one on this service or none".A service can be commented out by placing the number sign
#at the beginning of the name like this:OptsDepends = ( fooA #fooB fooC ) - The name of any valid service. A service declared as optional dependencies is not mandatory. The parser will look the corresponding service:
Provide¶
Provide = ( network networking )
Defines one or more service aliases—alternate names under which this service can be referenced. These aliases behave like symbolic links, allowing the same service to be managed with different name.
-
mandatory: no
-
syntax: brackets
-
valid values:
- Any arbitrary name.
Conflict¶
Conflict = ( connmand networkmanager )
Defines one or more services that cannot run or be enabled simultaneously with this service. If a conflicting service is running, attempts to start this service will fail. Similarly, if a conflicting service is enabled, attempts to enable this service will be rejected.
-
mandatory: no
-
syntax: brackets
-
valid values:
- Any valid service name.
Supervision policy¶
These keys control what the supervisor does with the process: whether it starts
on boot, and whether it gets a logger. (Readiness signalling, the crash budget and
the stop signal live in [Start] / [Stop] — see
Notify, MaxDeath and DownSignal.)
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
Options |
brackets | no | log on |
opt-in/out behaviours (currently: the logger) |
Flags |
brackets | no | — | down (start manually) / earlier (start with the scandir) |
Options¶
Options = (log)
Configures optional behaviors for the service. Wrap options in parentheses for multiple entries.
-
mandatory: no
-
syntax: brackets
-
valid values:
-
log : automatically create a logger for the service. This is default. The logger will be created even if this options is not specified. If you want to avoid the creation of the logger, prefix the options with an exclamation mark:
Options = ( !log )The behavior of the logger can be configured in the corresponding section—see [Logger].
-
Flags¶
Flags = (down earlier)
-
mandatory: no
-
syntax: brackets
-
valid values:
- down: This will create the down file used by the supervisor. Once this file was created the default state of the service will be considered down, not up: the service will not automatically be started until it receives a 66 start command. Without this file the default state of the service will be up and started automatically.
- earlier: This set the service as an earlier service meaning starts the service as soon as the scandir is up.
Deprecated [Main] keys (moved)¶
Several keys that describe a transition or the process execution used to live in
[Main]; each now belongs to the section of its domain. They are still accepted in
[Main] for backward compatibility, but emit a deprecation warning at parse time and
will be removed from [Main] in a future release. When a key is declared both in
[Main] and in its canonical section, the canonical section wins.
Deprecated in [Main] |
Declare instead |
|---|---|
Notify |
Notify in [Start] |
MaxDeath / MaxDeathInterval |
MaxDeath / MaxDeathInterval in [Start] |
DownSignal |
DownSignal in [Stop] |
StdIn / StdOut / StdErr |
StdIn / StdOut / StdErr in [Execute] |
TimeoutStart |
Timeout in [Start] |
TimeoutStop |
Timeout in [Stop] |
Permissions & files¶
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
User |
brackets | no | current process owner | users allowed to manage the service |
CopyFrom |
brackets/path | no | — | files/dirs copied verbatim into the service directory |
InTree |
inline | no | — | tree the service is activated in |
User¶
User = ( root )
Specifies the system user(s) list allowed to manage and operate the service. If not defined, defaults to the current process owner's username. 66 automatically distinguishes between user services and root services based on their installation paths. By default, only the specified users can start, stop, or interact with the service
-
mandatory: no
-
syntax: brackets
-
valid values :
- Any valid user of the system. If you don't know in advance the name of the user who will deal with the service, you can use the term
user. In that case every user of the system will be able to deal with the service. You can also use the@Uidentifier to be more specific.
(!) Be aware that
rootis not automatically added for a user service. If you don't declarerootin this field, you will not be able to use the service even withrootprivileges. - Any valid user of the system. If you don't know in advance the name of the user who will deal with the service, you can use the term
CopyFrom¶
CopyFrom = (./config /etc/default/service)
Verbatim copy directories and files on the fly to the main service destination. When dealing with directories, it copies all found files and directories recursively. In case of file, it copies it to the root of the service directory.
-
mandatory: no
-
valid values:
-
Any files or directories. It accepts absolute or relative path.
CopyFrom = ( data ./.env /etc/resolv.conf)
Note:
66version must be higher than 0.3.0.1. -
InTree¶
InTree = my-tree
Automatically activate the service within a named service tree. If a corresponding seed file exists, it will be applied.
-
mandatory: no
-
syntax: inline
-
valid values :
- Any name.
The service will automatically be activated at the tree name set in the InTree key value.
Note: If a corresponding seed file exist on your system, its will be used to create and configure the tree.
Event source keys — Type = event only¶
The following keys are valid only when
Typeisevent. They configure an event source: a non-supervised object that raises events for the event system. They have no meaning for aclassic/oneshot/moduleservice — such a service becomes an event reactor through the separate[Event]section instead. See 66-event for what each family does at runtime.
The EventType selects the family; the remaining keys depend on it:
EventType |
Companion keys |
|---|---|
inotify |
Watch (path) + On (inotify events) |
schedule |
Expression (cron) + optional Timezone |
timer |
Every (interval) |
EventType¶
EventType = inotify
Selects the family of a Type = event source. It is distinct from Type; on a reactor the same key lives in the [Event] section instead.
-
mandatory: yes for a
Type = eventsource; not valid otherwise. -
syntax: inline
-
valid values:
- inotify : watch a filesystem path — pair with
WatchandOn. - schedule : fire on a cron/calendar
Expression, optionally in aTimezone. - timer : fire on a relative interval
Every.
- inotify : watch a filesystem path — pair with
Watch¶
Watch = /etc/resolv.conf
The filesystem path an inotify source watches, paired with On.
-
mandatory: yes for an
inotifysource; not valid otherwise. -
syntax: inline
-
valid values:
- Any absolute path to an existing file or directory.
-
notes:
IN_CREATE/IN_DELETE/IN_MOVED_*only fire for entries inside a watched directory, not for a watched file. A tool that replaces a file atomically (write-temp then rename — dhcpcd, certbot, most editors) does not raiseIN_MODIFYon it; watch the directory (IN_CREATE/IN_MOVED_TO) or the file itself withIN_MOVE_SELF/IN_DELETE_SELF.
On¶
On = ( IN_CLOSE_WRITE IN_MOVE_SELF )
The inotify(7) event(s) an inotify source reacts to on its Watch path. (The reactor key On in the [Event] section is a different vocabulary.)
-
mandatory: yes for an
inotifysource; not valid otherwise. -
syntax: brackets — parentheses required, even for a single value.
-
valid values:
- One or more kernel
inotifyconstants:IN_ACCESS,IN_MODIFY,IN_ATTRIB,IN_CLOSE_WRITE,IN_CLOSE_NOWRITE,IN_OPEN,IN_MOVED_FROM,IN_MOVED_TO,IN_CREATE,IN_DELETE,IN_DELETE_SELF,IN_MOVE_SELF, plus the shorthandsIN_MOVE(IN_MOVED_FROM+IN_MOVED_TO),IN_CLOSE(IN_CLOSE_WRITE+IN_CLOSE_NOWRITE) andIN_ALL_EVENTS. They map straight to the watch mask.
- One or more kernel
Expression¶
Expression = "0 0 3 * * ?"
The cron expression of a schedule source. The engine is a Quartz-style scheduler — not classic 5-field Vixie cron.
-
mandatory: yes for a
schedulesource; not valid otherwise. -
syntax: quotes
-
valid values — a cron expression of 5, 6 or 7 space-separated fields:
[seconds] minutes hours day-of-month month day-of-week [year]Fields Layout 5 min hour dom month dow(seconds default to0)6 sec min hour dom month dow7 sec min hour dom month dow yearRanges
0-59/0-59/0-23/1-31/1-12/0-7/1970-2200. Months acceptJAN..DEC, days acceptSUN..SAT(case-insensitive,0= Sunday). Operators:*,-/plus Quartz?(no specific value),L(last),L-<n>,LW(last weekday),<n>W(nearest weekday),<n>L(last weekday-n),<n>#<m>(m-th weekday-n,6#3= 3rd Friday). Macros:@yearly/@annually,@monthly,@weekly,@daily/@midnight,@hourly,@minutely,@secondly. -
notes:
- You must put
?on either day-of-month or day-of-week — they cannot both carry a value. Even in 5 fields,"0 3 * * *"is rejected; write"0 3 * * ?". - There is no
@rebootmacro. - The expression is validated at 66 parse time; an invalid one fails with a clear error rather than silently at runtime.
- You must put
Timezone¶
Timezone = Europe/Paris
The timezone the Expression of a schedule source is evaluated in.
-
mandatory: no; valid only for a
schedulesource. -
syntax: inline
-
valid values:
- Any IANA timezone name (
UTC,Europe/Paris, …), up to 255 characters. When omitted, the schedule is evaluated in UTC.
- Any IANA timezone name (
Every¶
Every = 30s
The period of a timer source: a relative, monotonic interval that fires again and again, unaffected by wall-clock changes.
-
mandatory: yes for a
timersource; not valid otherwise. -
syntax: inline
-
valid values:
- A positive whole number with an optional unit suffix —
s(or none) for seconds,mminutes,hhours,ddays. Examples:30s,5m,1h,90. The value must be at least one second.
- A positive whole number with an optional unit suffix —
Section [Start]¶
This section is mandatory. It defines how the service is started.
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
Execute |
brackets | yes | — | the command(s) that start the service |
RunAs |
inline/simple-colon | no | service owner | drop privileges to a user before exec |
Timeout |
uint | no | 0 (no timeout) |
max time for the start transition, in ms |
Notify |
uint | no | — | readiness-notification file descriptor |
MaxDeath |
uint | no | 5 |
crash budget before failed (0 = never fail) |
MaxDeathInterval |
uint | no | 30000 |
crash-counting window, in ms |
Build (deprecated)¶
The build type is no longer set by this key — it is detected automatically from the Execute field. An Execute whose first non-blank line is a shebang (#!…) is treated as a custom script and run verbatim in that interpreter; otherwise it is an execline script. See Appendix B — The Execute key in depth.
Build is still accepted for now but ignored: declaring it only emits a deprecation warning at parse time. Remove it from your frontends.
RunAs¶
RunAs = oblive
-
mandatory: no
-
syntax: inline,simple-colon
-
valid value:
-
Any valid user name set on the system or valid uid:gid number.
RunAs = oblive RunAs = 1000:19 # if uid is not specified, # the uid of the owner of the process # is pick by default RunAs = :19 # if gid is not specified, # the gid of the owner of the process # is pick by default RunAs = 1000:This will pass the privileges of the service to the given user before starting the run script of the service.
Note: (!) The service needs to be first started with root if you want to hand over privileges to a user. Only root can pass on privileges. This field has no effect for other use cases.
-
Execute¶
Execute = ( /usr/bin/auditd -f )
-
mandatory: yes (!)
-
syntax: brackets
-
valid value:
- The command to execute when starting the service.
Note: The field will be used as is. No changes will be applied at all except in
customcase (see Appendix B). It's the responsibility of the author to make sure that the content of this field is correct.
Timeout (Start)¶
Timeout = 2000
66 kills the service
and reports the transition as failed.
-
mandatory: no
-
syntax: uint
-
valid values:
- Any valid number, in milliseconds. The default is
0, which means no start timeout — the service may take as long as it needs to come up.
The former name
TimeoutStartis still accepted here as a deprecated alias ofTimeout; it emits a deprecation warning and will be removed in a future release. - Any valid number, in milliseconds. The default is
Notify¶
Notify = 3
Enables readiness notification: declares the file descriptor on which the service announces it is ready.
-
mandatory: no
-
syntax: uint
-
valid values:
- A file descriptor number of
3or higher (values below3are rejected).
The value is the number of the file descriptor the service writes its readiness notification to — usually a dedicated descriptor such as
3, matching the option your daemon uses to announce its readiness. Standard output (descriptor1) is normally unsuitable here, as it is redirected to the logger. The value is stored in the service's resolve. When the service is started, 66-supervise hands the process a pipe on that descriptor and waits for the service to write to it before reporting the service up and ready. - A file descriptor number of
MaxDeath¶
MaxDeath = 5
Sets the crash budget: the number of times the service may die within a MaxDeathInterval window before the supervisor gives up and declares it failed, stopping any further automatic restart.
-
mandatory: no
-
syntax: uint
-
valid value:
- Any number from
0to16. The default is5. A value of0disables the budget: the service is restarted indefinitely and is never declared failed for crash-looping.
Only an actual run-then-die counts against the budget — the service did execute its
runscript, then exited or was signalled while still wanted up. A commanded stop never counts, and a service that never managed to exec itsrunscript (missing interpreter, unmounted filesystem, …) is reported as exec failed and retried with a progressive backoff without ever consuming the budget. Once the budget is exhausted the service stays failed until a new start resets the counter and relaunches it.Each automatic restart is throttled by a minimum delay of one second, so a crash-looping service consumes its budget at a rate of at most one death per second.
- Any number from
MaxDeathInterval¶
MaxDeathInterval = 30000
The length, in milliseconds, of the time window over which MaxDeath crashes are counted.
-
mandatory: no
-
syntax: uint
-
valid value:
- Any valid number, in milliseconds. The default is
30000(30 seconds).
The window is measured on a monotonic clock and starts at the first counted death. If the service reaches
MaxDeathdeaths before the window elapses, it is declared failed. Otherwise — the window expires with fewer deaths — the window is re-armed: the next death starts a fresh window with the count reset to one. The measurement lives in volatile runtime state and is cleared on reboot. Because restarts are throttled to roughly one per second, exhausting the budget takes on the order ofMaxDeathseconds; settingMaxDeathIntervalmuch below that makes the failed state effectively unreachable through crash-looping alone. - Any valid number, in milliseconds. The default is
Section [Stop]¶
This section is optional. It handles the stop process of the service.
It shares the RunAs and Execute keys
with [Start] — they behave identically — plus its own
Timeout and DownSignal keys.
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
Execute |
brackets | no | signal the process | the command(s) that stop the service |
RunAs |
inline/simple-colon | no | service owner | drop privileges to a user before exec |
Timeout |
uint | no | 0 (no timeout) |
max time for the stop transition, in ms |
DownSignal |
inline | no | SIGTERM |
signal used to stop/restart the process |
Timeout (Stop)¶
Timeout = 5000
Specifies the maximum time (in milliseconds) the service's stop sequence may
take. If the stop transition — the stop/finish script — does not complete within
this time, 66 kills the service.
-
mandatory: no
-
syntax: uint
-
valid values:
- Any valid number, in milliseconds. The default is
0, which means no stop timeout — the stop script may run as long as it needs.
The former name
TimeoutStopis still accepted here as a deprecated alias ofTimeout; it emits a deprecation warning and will be removed in a future release. - Any valid number, in milliseconds. The default is
DownSignal¶
DownSignal = SIGTERM
Specifies which signal to send when stopping or reloading the service.
-
mandatory: no
-
syntax: inline
-
valid value:
- The name or number of a signal.
This will create the file down-signal which is used to kill the supervised process when a reload, restart or stop command is used. If the file does not exist
SIGTERMwill be used by default.
Section [Logger]¶
This section is optional and controls the behavior of the default logging system used by 66, which is handled by its native 66-log program.
It will only have effects if value log was not prefixed by an exclamation mark to the Options key in the [Main] section. Additionally, the StdIn or StdOut keys from the [Execute] section must be set to 66log, or these keys must not be defined at all.
This section also accepts the RunAs and
Execute keys (behaving as in [Start]), plus a single
Timeout key giving the logger's start timeout. The logger has no stop
transition — it runs a single script and is brought down by a signal — so there is
no stop timeout, even if an Execute is declared. Timeout is not mandatory; when
omitted, the default behaviour applies. The former name TimeoutStart is still
accepted as a deprecated alias of Timeout (it warns and will be removed in a future
release); TimeoutStop is no longer a valid logger key.
The keys specific to the logger:
| Key | Syntax | Required | Default | Role |
|---|---|---|---|---|
Backup |
uint | no | 3 |
number of rotated log files kept |
MaxSize |
uint | no | 1000000 |
rotation threshold, in bytes (4096–268435455) |
Timestamp |
inline | no | iso |
tai / iso / none |
Backup¶
Backup = 3
-
mandatory: no
-
syntax: uint
-
valid value:
-
Any valid number.
The log directory will keep value files. The next log to be saved will replace the oldest file present. By default
3files are kept.
-
MaxSize¶
MaxSize = 1000000
-
mandatory: no
-
syntax: uint
-
valid value:
-
Any valid number.
A new log file will be created every time the current one approaches value bytes. By default, filesize is
1000000; it cannot be set lower than4096or higher than268435455.
-
Timestamp¶
Timestamp = iso
Specifies timestamp format prefixed to each log entry. If not specified, it defaults to iso (configurable at compile time).
-
mandatory: no
-
syntax: inline
-
valid value:
-
tai
The logged line will be preceded by a TAI64N timestamp (and a space) before being processed by the next action directive.
-
iso
The selected line will be preceded by a ISO 8601 timestamp for combined date and time representing local time according to the systems timezone, with a space (not a
T) between the date and the time and two spaces after the time, before being processed by the next action directive. -
none
The logged line will not be preceded by any timestamp.
-
Two possible examples for the [Logger] section:
[Logger]
RunAs = user
Timeout = 10000
Backup = 10
Timestamp = iso
[Logger]
Backup = 10
Section [Environment]¶
This section is optional.
A file containing the key=value pair(s) will be created by default at /etc/66/conf/name_of_service directory. The default can also be changed at compile-time by passing the -D sysadmin-service-conf-dir=DIR option to meson setup.
Any key=value pair¶
DirRun=/run/openntpd
-
mandatory: no
-
syntax: pair
-
valid value:
-
You can define any variables that you want to add to the environment of the service. For example:
[Environment] dir_run=/run/openntpd cmd_args=-d -sThe
!character can precede the value. Ensure no space exists between the exclamation mark and the value. This action explicitly avoids setting the value of the key for the runtime process but only applies it at the start of the service. For instance, the following valid example unsets thekey=valuepairdir_run=!/run/openntpdfrom the general environment variables of the service.the following syntax is valid
where this one is not[Environment] dir_run=!/run/openntpd cmd_args = !-d -s[Environment] dir_run=! /run/openntpd cmd_args = ! -d -sRefers to execl-envfile for further information.
-
ImportFile¶
ImportFile=/etc/66/init.conf
The ImportFile variable is recognized by 66 and treated as a key=value pair, similar to other environment variables. However, ImportFile itself is not exported to the environment.
The target file must adhere to the environment definition syntax specified in the file syntax guidelines.
-
mandatory: no
-
syntax: path
-
valid value:
-
Any valid absolute file path can be specified. The
ImportFilevariable can be defined multiple times. For example:[Environment] dir_run=/run/openntpd ImportFile=/etc/66/init.confThe
!character has no effect onImportFile.ImportFileprocessing occurs at the end of the environment setup. If a key is defined both in the[Environment]section and in a file specified byImportFile, the value from theImportFiletakes precedence.For multiple
ImportFiledeclarations, the last declared file takes precedence for any duplicate keys found across the specified files.identifier is still also interpreted. For example:
[Environment] dir_run=/run/openntpd ImportFile=/etc/66/init.conf ImportFile=@H/.66/environment/my.conf
-
Section [Regex]¶
This section is optional.
It will only have an effect when the service is a module type—see the section Module service creation.
identifier are replaced before applying the regex section.
| Key | Syntax | Required | Role |
|---|---|---|---|
Configure |
quotes | no | arguments passed to the module's configure script |
Directories |
pair in brackets | no | rename module subdirectories by regex |
Files |
pair in brackets | no | rename module files by regex |
InFiles |
colon in brackets | no | in-file regex replacements |
Configure¶
Configure = "--enable-feature"
Arguments passed to the module’s configure script.
-
mandatory: no
-
syntax: quotes
-
valid value:
- You can define any arguments to pass to the module's configure script.
Directories¶
Directories = ( DM=sddm )
Regex-based renaming rules for module subdirectories. Each entry is regex=replacement.
-
mandatory: no
-
valid value:
-
Any
key=valuepair where key is the regex to search on the directory name and value the replacement of that regex. For example:Directories = ( DM=sddm TRACKER=consolekit )Where the module directory contains two sub-directories named use-DM and by-TRACKER directories. It will be renamed as use-sddm and by-consolekit respectively.
-
Files¶
Files = ( servicename=newname )
Regex-based renaming rules for files. Each entry is regex=replacement.
-
mandatory: no
-
valid value:
- Reacts exactly as Directories field but on files name instead of directories name.
InFiles¶
InFiles = ( :mount-tmp:args=-o noexec )
In-file regex replacements for module files. Use :filename:regex=replacement or ::regex=replacement for all files.
-
mandatory: no
-
valid value:
-
Any valid filename between the double colon with any
key=valuepair where key is the regex to search inside the file and value the replacement of that regex. The double colon must be present but the name between it can be omitted. In that case, thekey=valuepair will apply to all files contained on the module directories and to all keys (regex) found inside the same file.For example:InFiles = ( :mount-tmp:args=-o noexec ::user=@I )- It replaces first the term
@Iby the name of the module. - It opens the file named mount-tmp, search for the args regex and replaces it by the value of the regex.
- It opens all files found on the module directory and replaces all regex 'user' found by the name of the module in each file.
- It replaces first the term
-
Section [Execute]¶
This section is optional. It configures tasks executed just before exec for
the service’s start and stop processes: resource limits, process attributes,
Linux capabilities and standard I/O redirection.
How resource limits are applied. Each LimitXXX key sets both the soft
(rlim_cur) and hard (rlim_max) limit: 66 reads the current limits with
getrlimit(), adjusts the hard limit for root-owned services if needed, caps the
soft limit to the hard limit for non-root services, and applies them with
setrlimit(). If a limit is zero, no change is made. Every LimitXXX accepts
unlimited to set the corresponding RLIMIT_* to RLIM_INFINITY. For
unprivileged (non-root) services, unlimited or any value above the current hard
limit is capped at rlim_max; raising a limit beyond rlim_max requires root or
CAP_SYS_RESOURCE. Linux-specific limits are ignored where the kernel does not
support them.
Resource limits¶
All keys below use uint syntax, are optional, and accept unlimited.
| Key | RLIMIT_* |
Unit / range | Notes |
|---|---|---|---|
LimitAS |
RLIMIT_AS |
bytes | address space (virtual memory) |
LimitCORE |
RLIMIT_CORE |
bytes | core dump size; 0 disables core dumps |
LimitCPU |
RLIMIT_CPU |
seconds | exceeding it sends SIGXCPU |
LimitDATA |
RLIMIT_DATA |
bytes | data segment; affects malloc() |
LimitFSIZE |
RLIMIT_FSIZE |
bytes | max file size; exceeding it sends SIGXFSZ |
LimitLOCKS |
RLIMIT_LOCKS |
count | file locks — Linux only |
LimitMEMLOCK |
RLIMIT_MEMLOCK |
bytes | locked memory; affects mlock() |
LimitMSGQUEUE |
RLIMIT_MSGQUEUE |
bytes | POSIX message queues — Linux only |
LimitNICE |
RLIMIT_NICE |
-20..19 |
nice ceiling — Linux only; see details |
LimitNOFILE |
RLIMIT_NOFILE |
count | open fds (files, sockets, pipes) |
LimitNPROC |
RLIMIT_NPROC |
count | processes for the user (not just this service) |
LimitRTPRIO |
RLIMIT_RTPRIO |
0..100 |
real-time priority — Linux only; 0 disables |
LimitRTTIME |
RLIMIT_RTTIME |
microseconds | real-time CPU time — Linux only |
LimitSIGPENDING |
RLIMIT_SIGPENDING |
count | queued signals — Linux only |
LimitSTACK |
RLIMIT_STACK |
bytes | stack size; affects recursion depth |
LimitNICE details¶
Values are an integer between -20 (highest priority) and 19 (lowest priority).
Lower values give higher CPU priority; higher values give lower priority. Numeric
values are adjusted to the hard limit if exceeded (e.g. -20 may be capped to 0
if ulimit -He is 20). Setting negative nice values may require CAP_SYS_NICE
for unprivileged processes. Only available on Linux; ignored on systems lacking
RLIMIT_NICE.
Process attributes¶
| Key | Syntax | Default | Role |
|---|---|---|---|
BlockPrivileges |
boolean | false |
set PR_SET_NO_NEW_PRIVS |
UMask |
uint (octal) | system default | file creation mask |
Nice |
uint | system default | scheduling priority (-20..19) |
ChangeDirectory |
path | parent's cwd | working directory (chdir()) |
CapsBound |
brackets | unchanged | capability bounding set (root only) |
CapsAmbient |
brackets | none | ambient capabilities |
BlockPrivileges¶
BlockPrivileges = true
Enables the Linux PR_SET_NO_NEW_PRIVS flag via prctl(), preventing the service process and its children from gaining additional privileges (e.g., via setuid binaries or capability inheritance).
-
mandatory: no
-
syntax: boolean
-
valid values:
- A boolean value
-
notes:
Once set, cannot be unset for the process or its children.
UMask¶
UMask = 022
Sets the file creation mask for the service process via umask(), controlling default permissions for newly created files and directories. The value is specified in octal notation, determining which permission bits are masked from the default mode.
-
mandatory: no
-
syntax: uint
-
valid values:
-
An octal number between
000and777(e.g.,022,002,077). -
Undefined: Defaults to system-wide configuration.
-
Nice¶
Nice = -10
Sets the CPU scheduling priority (nice value) for the service process via setpriority(), affecting how the kernel allocates CPU time. Lower values increase priority; higher values decrease it.
-
mandatory: no
-
syntax: uint
-
valid values:
-
An integer between
-20(highest priority) and19(lowest priority). -
Undefined: Defaults to system-wide configuration.
-
-
notes:
Negative values (e.g.,
-10) requireCAP_SYS_NICEfor unprivileged services (non-root users) or root privileges.Must be within the
RLIMIT_NICElimit set byLimitNICE.Affects the service process and its children.
ChangeDirectory¶
ChangeDirectory = /var/lib/myservice
Sets the working directory for the service process via chdir(), affecting the default directory for file operations (e.g., opening files with relative paths).
-
mandatory: no
-
syntax: path
-
valid values:
-
Any valid absolute file path can be specified.
-
Undefined: Inherits the working directory from the parent process (default, typically the supervision directory).
-
-
notes:
The directory must exist and be accessible (readable and executable) by the service’s user. Permission or non-existent directory errors cause the service to fail with a logged warning.
Affects the service process and its children.
CapsBound¶
CapsBound = (CAP_SYS_NICE CAP_CHOWN)
Defines the Linux capabilities allowed in the capability bounding set for a root-owned service’s process. This setting controls which special permissions (like adjusting process priorities or changing file ownership) the service can use, restricting it to only the listed capabilities or excluding specific ones.
-
mandatory: no
-
syntax: brackets
-
valid values:
-
A space-separated list of capability names in parentheses, such as (
CAP_SYS_NICECAP_CHOWNCAP_DAC_OVERRIDE). -
Capability names can be prefixed with
!to exclude them, allowing all other capabilities. For example, (CAP_NET_ADMIN!CAP_SYS_ADMINCAP_MAC_OVERRIDE!CAP_SYS_RESOURCE) allows all capabilities exceptCAP_SYS_ADMINandCAP_SYS_RESOURCE. -
Valid capability names include
CAP_SYS_NICE,CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_SYS_ADMIN, and others (see Linux documentation for the full list). -
Undefined: No changes are made to the bounding set, and the service uses the system’s default permissions.
-
-
notes:
Only applies to services running as the root user. For non-root services, this setting is silently ignored.
Clears all existing capabilities in the bounding set before applying the listed ones, ensuring only specified capabilities are allowed.
If any capability name is prefixed with
!, the list is interpreted as allowing all capabilities except those marked with!. For example, (CAP_NET_ADMIN!CAP_SYS_ADMIN) allows all capabilities exceptCAP_SYS_ADMIN. Where doing, (CAP_SYS_NICECAP_CHOWN) restricts the bounding set to onlyCAP_SYS_NICEandCAP_CHOWN.Invalid capability names are ignored, and a warning is logged when the service configuration is parsed.
To allow privileges to be dropped, it is necessary to set
CAP_SETUIDandCAP_SETGIDto the capability bounding set if you use theRunAskey.Requires Linux kernel version
5.6or later.
CapsAmbient¶
CapsAmbient = (CAP_SYS_NICE)
Specifies Linux capabilities that a service and its child processes automatically retain, even when starting new programs. This allows permissions, such as adjusting process priorities, to be passed to child processes without requiring root privileges.
-
mandatory: no
-
syntax: brackets
-
valid values:
-
A space-separated list of capability names in parentheses, such as
(CAP_SYS_NICE CAP_CHOWN CAP_DAC_OVERRIDE). -
Capability names can be prefixed with
!to exclude them, allowing all other capabilities. For example,(CAP_SYS_NICE !CAP_SYS_ADMIN)includes all capabilities exceptCAP_SYS_ADMIN. -
Undefined: No ambient capabilities are set, and child processes inherit no special permissions.
-
-
notes:
Applies to both root-owned services and non-root services.
The bounding set must contain at least
CAP_SETPCAPcapability and each listed capability. If not, it is skipped, and a warning is logged. IfCAP_SETPCAPis not in bounding set, the process dies.For root-owned services, if
CapsBoundis not set, the service checks the system’s current set of allowed permissions to decide which capabilities can be used. IfCapsBoundis set, only the capabilities listed inCapsBoundare considered. For example, ifCapsBound = (CAP_SYS_NICE)andCapsAmbient = (CAP_DAC_OVERRIDE), theCAP_DAC_OVERRIDEcapability will be skipped because it is not in theCapsBoundlist, and a warning will be logged.Requires Linux kernel version
5.6or later.
Standard I/O redirection¶
StdIn, StdOut and StdErr control where the service's three standard streams
go. They accept a plain keyword or, for some, a type:/path form (simple-colon).
See Standard IO redirection for the full model.
| Key | Default | Common values |
|---|---|---|
StdIn |
66log |
66log · null · close · parent · tty:/path |
StdOut |
66log |
66log · file:/path · syslog · console · null · close · parent · tty:/path |
StdErr |
inherit |
inherit · file:/path · syslog · console · null · close · parent · tty:/path |
StdIn¶
StdIn = null
Controls standard I/O redirection for the standard input entries.
-
mandatory: no
-
syntax: inline,simple-colon
-
valid values:
- tty:/path/to/tty: Redirects Standard Input to the given tty specified by the path and try to become the controlling process of the terminal. The path must be absolute and exist. If the terminal is already being controlled by another process and the operation returns an EPERM failure, 66 will warn the user and continue its execution. If the failure is other than EPERM, it will terminate.
- 66log: Redirects Standard Input to the socket of the
66-logprogram. This is the default. - null: Redirects Standard Input to
/dev/null - parent: This is a no-op redirection. The Standard Input is inherited from the parent process, meaning the 66-supervise program.
- close: Close the Standard Input.
StdOut¶
StdOut = 66log
Controls standard I/O redirection for the standard output entries.
-
mandatory: no
-
syntax: inline,simple-colon
-
valid values:
- tty:/path/to/tty: Redirects Standard Output to the given tty specified by the path. The path must be absolute and exist. It does not try to take control of the terminal.
- file:/path/to/file: Redirects Standard Output to the given file specified by the path. The path must be absolute. If the directory of the file and the file itself do not exist, 66 will create it. In that case, the directory will get
0755permissions and the file will be set with0666permissions. - console: Redirects Standard Output to the active console. It does not try to take control of the console.
- 66log: Redirects Standard Output to the socket of the
66-logprogram. This is the default. - syslog: Redirects Standard Output to the
/dev/logsocket. - null: Redirects Standard Output to
/dev/null. - parent: This is a no-op redirection. The Standard Output is inherited from the parent process, meaning the
66-superviseprogram. - close: Closes the Standard Output.
StdErr¶
StdErr = inherit
Controls standard I/O redirection for the standard error entries.
-
mandatory: no
-
syntax: inline,simple-colon
-
valid values:
- tty:/path/to/tty: Redirects Standard Error to the given tty specified by the path. The path must be absolute and exist. It does not try to take control of the terminal.
- file:/path/to/file: Redirects Standard Error to the given file specified by path. Path must be absolute. If the directory of file and the file itself doesn't exist, 66 create it. In that case, the directory get
0755as permissions and the file is set with0666as permissions. - console: Redirects Standard Error to the active console. It does not try to take control of the console.
- syslog: Redirects Standard Error to the
/dev/logsocket. - null: Redirects Standard Error to
/dev/null. - parent: This is a no-op redirection. The Standard Error is inherited from the parent process, meaning the
66-superviseprogram. - inherit: Duplicates the Standard Error to the Standard Output. This is the default.
- close: Closes the Standard Error.
Section [Event]¶
This section is optional. It turns an ordinary classic, oneshot or module service into an event reactor: when its trigger fires, the service runs a 66 command on itself (Do) and/or raises a named event (Emit). A frontend carries at most one rule — a service is either a reactor or a Type = event source, never both. See 66-event for the full model and the runtime behaviour, and 66-eventd for the daemon that runs the rules.
| Key | Syntax | Required | Role |
|---|---|---|---|
EventType |
inline | yes | trigger family: service/signal/user/inotify/schedule/timer |
From |
brackets | yes (except user) |
the source(s) the reactor subscribes to |
On / OnAll |
brackets | yes for service/signal/user |
the trigger condition(s) |
Do |
inline | one of Do/Emit |
66 command run on itself when it fires |
Emit |
inline | one of Do/Emit |
user event raised when it fires |
Propagate |
boolean | true |
let the action reach the dependency chain |
EventType¶
EventType = service
Selects which family of trigger the reactor subscribes to, and therefore which On vocabulary applies. Same key, same values as the source EventType in [Main], but placed here for a reactor.
-
mandatory: yes for a reactor.
-
syntax: inline
-
valid values:
service,signal,user,inotify,schedule,timer.- service : react to the status transitions (up/down/crash/…) of a supervised service named in
From. - signal : react to a signal routed by
66to a supervised service named inFrom. - user : react to a name raised by 66 emit or by another reactor's
Emit. Auserreactor is sourceless — noFrom. - inotify / schedule / timer : react to the
Type = eventsource named inFrom. The condition lives in the source, so these carry noOn.
- service : react to the status transitions (up/down/crash/…) of a supervised service named in
From¶
From = ( rabbitmq )
The source(s) the reactor subscribes to. Always explicit — sources are never inferred from On.
-
mandatory: yes for every reactor except
user(which is sourceless). -
syntax: brackets
-
valid values:
- The name of a service. For
service/signalit is a supervised service; forinotify/schedule/timerit is the name of theType = eventsource.
- The name of a service. For
-
notes:
Each
Fromsource also becomes a dependency of the reactor, so66starts (or arms) the source before it arms the reactor. Consequently66-eventdreads the source's current state at arm time: a reactor whose source is already in the awaited state fires immediately, instead of waiting for the next transition. Likewise,66 free <source>disarms the reactors that depend on it.
On / OnAll¶
On = ( down )
OnAll = ( auth:up db:up )
The trigger condition(s) of a service, signal or user reactor. Use exactly one of the two keys:
On— a single condition, or a bracketed list treated as OR (fires if any listed condition matches).-
OnAll— an AND over the current states: fires only when all listed conditions hold at once. Valid only forserviceandsignalreactors; auserreactor (whose conditions are momentary names, not states) usesOnonly. -
mandatory: yes for
service,signalanduserreactors; forbidden forinotify/schedule/timerreactors (the condition is the source's ownOn). -
syntax: brackets — parentheses required, even for a single value.
-
valid values — depend on
EventType:- service : a status state word —
down,starting,up,stopping,finishing,restarting,done,failed— or a status result word —success,exited,signaled,timeout-start,timeout-stop,crash-limit,exec-failed. Two results take an argument:exited:<code>andsignaled:<SIG>(e.g.signaled:SIGKILL). These are exactly the words 66 status prints. (signaledmeans the process died from a signal, unlike asignalreactor which means a routed signal was received.) - signal : a signal name, e.g.
SIGHUP. - user : the emitted name, e.g.
backend-down.
- service : a status state word —
-
per-source form: in a list, a bare token (
up) applies to all sources ofFrom; aservice:conditiontoken (auth:up) scopes the condition to one named source, which must be a member ofFrom.
Do¶
Do = restart
The 66 command the reactor runs on itself when the trigger fires.
-
mandatory: no on its own, but a reactor must define at least one of
Do/Emit. -
syntax: inline
-
valid values: exactly one of
start,stop,restart,reload,reconfigure,free— the matching66command. Bare command only, no argument. -
notes:
The command is gated by the reactor's current state, mirroring the matching
66command (see 66-event). A service counts as active when it is up or done, and inert when it is down or failed.Do = startacts unless the service is already active;Do = stopandDo = reloadact on an active service and are inhibited on an inert one;Do = restartalways acts, converging to up even from down (like66 restart);reconfigure/freealways act.Do = reloadsignals a running process, so it is rejected at parse time on aoneshotreactor (a oneshot has no process to signal). It is valid on aclassicreactor and on amodulereactor, where it reaches the module's own services.
Emit¶
Emit = backend-down
Raises a user event of the given name when the trigger fires, independently of Do. This is how reactions chain: another reactor with EventType = user and On = ( <name> ) fires in turn (as would 66 emit <name>). A reactor may carry Do, Emit, or both.
-
mandatory: no on its own, but a reactor must define at least one of
Do/Emit. -
syntax: inline
-
valid values: any name. It matches the
Onof auserreactor.
Propagate¶
Propagate = false
Whether the Do command walks the dependency chain. Left out, the reaction is
the bare 66 command and nothing else. Set to false, the action carries -P, exactly as if
you had typed 66 restart -P <service>.
-
mandatory: no
-
syntax: boolean
-
valid values:
- A boolean value
-
notes:
Do = Xmeans what66 Xmeans: a reactor is a trigger, not a variant of the command. That is why propagation is the default.Which way the chain is walked depends on the verb, and so does what
falsecosts you. ADoofstop,restart,reload,reconfigureorfreereaches the services that depend on the reactor. Settingfalsekeeps the reaction to the service alone. ADo = startinstead reaches what the reactor depends on, andfalsethen starts it without bringing its dependencies up, rarely what you want.For instance, the case
falseis made for: aninotifyreactor reloadingpostgresqlwhenpg_hba.confchanges. Propagating would drag everyrequiredbyofpostgresqland their cascades along, SIGHUP-ing the whole application stack for one edited access control line.
Appendix A — Value syntax reference¶
The value of a key is parsed in a specific format depending on the key. Each key's description states which of the following formats it uses.
inline¶
An inline value. Must be on the same line with its corresponding key.
-
Valid syntax:
Type = classic Type=classic -
(!) Invalid syntax:
Type= classicquotes¶
A value between double-quotes. Must be on the same line with its corresponding key.
-
Valid syntax:
Description = "some awesome description" Description="some awesome description" -
(!) Invalid syntax:
Description= "some awesome description" Description = "line break inside a double-quote is not allowed"
brackets¶
Multiple values between parentheses (). Values need to be separated with a space. A line break can be used instead.
-
Valid syntax:
Depends = ( fooA fooB fooC ) Depends=(fooA fooB fooC) Depends=( fooA fooB fooC ) Depends= ( fooA fooB fooC ) -
(!) Invalid syntax:
Depends = (fooAfooBfooC)
uint¶
A positive whole number. Must be on the same line with its corresponding key.
-
Valid syntax:
Notify = 3 Notify=3 -
(!) Invalid syntax:
Notify= 3
path¶
An absolute path beginning with a forward slash /. Must be on the same line with its corresponding key.
-
Valid syntax:
ChangeDirectory = /etc/66 ChangeDirectory=/etc/66 -
(!) Invalid syntax:
ChangeDirectory=/a/very/ long/path
pair¶
Same as inline.
-
Valid syntax:
MYKEY = MYVALUE anotherkey=anothervalue anotherkey=where_value=/can_contain/equal/Character -
(!) Invalid syntax:
MYKEY= MYVALUE
colon¶
A value between double colons followed by a pair syntax. Must be one by line.
-
Valid syntax:
::key=value :filename:key=value -
(!) Invalid syntax:
::MYKEY= MYVALUE :: MYKEY=MYVALUE ::key=value :filename:anotherkey=anothervalue
simple-colon¶
A values separated by a colon. Must be on the same line with its corresponding key.
-
Valid syntax:
RunAs = 1000:19 -
(!) Invalid syntax:
RunAs = 1000: 19
boolean¶
A value specifying a true state for the key. Must be on the same line with its corresponding key.
-
Valid syntax:
BlockPrivileges = true BlockPrivileges = True BlockPrivileges = TRUE BlockPrivileges = 1 BlockPrivileges = false BlockPrivileges = False BlockPrivileges = FALSE BlockPrivileges = 0 -
(!) Invalid syntax:
BlockPrivileges = true BlockPrivileges = -
note: For code simplicity and rapidity, setting e.g.
key = Tis strictly equivalent tokey = Trueorkey = TRUEas the parser only checks the first letter of the string value.
Appendix B — The Execute key in depth¶
The Execute key can be written in any language. Make the first non-blank line of the field a shebang (#!/usr/bin/bash, #!/usr/bin/python3, …): 66 detects it and treats the script as custom, running it verbatim in that interpreter. Without a shebang, the field is an execline script. For example, to write your Execute field with bash:
Execute = (#!/usr/bin/bash
echo "This script displays available services"
for i in $(ls /usr/share/66/service); do
echo "daemon : ${i} is available"
done
)
This is an unnecessary example but it shows how to construct this use case. The resulting file will be :
#!/usr/bin/bash
echo "This script displays available services"
for i in $(ls /usr/share/66/service); do
echo "daemon : ${i} is available"
done
The parser duplicates exactly what appears between ( and ), preserving all characters as they are. However, it removes any carriage return (\r), tab (\t), space, or newline (\n) located between the opening parenthesis and the # of the shebang declaration. No other characters are permitted in this span. For instance, if you write
Execute = (
#!/bin/bash
echo hello world!
)
the final result will be
#!/bin/bash
echo hello world!
ensuring that the very first line of the script is the declaration of the shebang to avoid an Exec format error.
Substitution in Execute¶
In the execline format, a ${...} written in Execute is replaced when the service starts, by
66-execute, against the environment that service is about to receive — not only against
the [Environment] section of the frontend. Every layer is assembled first, then the whole is
expanded once:
environment inherited from the scandir
< the [Environment] section
< the service configuration file (66 configure)
< the ImportFile files
< /run/66/environment/<uid>/ <- 66 env
A later layer overrides an earlier one (see Precedence), and a
${...} resolves to the value that survives that merge. A frontend may therefore reference a
variable it never declares — a session variable carried by the supervision tree, or a value
published with 66 env:
[Start]
Execute = (
gnome-keyring-daemon --foreground --control-directory=${XDG_RUNTIME_DIR}/keyring
)
A name no layer carries is left as typed: ${NOPE} reaches the command line as those six
characters, it is not turned into an empty string. Write \${NOPE} when that literal form is
what the program expects.
Note that in a custom (shebang) script, variables will not be replaced by their corresponding environment values within the script, unlike the behavior with the execlineb script format.
identifier is still also interpreted even in custom script.
This same behavior applies to the [Logger] section. Also, The fields Backup, MaxSize and Timestamp will have no effect in a custom case. You need to explicitly define the program to use the logger and the options for it in your Execute field.
Appendix C — The Version key in depth¶
The Version key supports formats inspired by semantic versioning (e.g., "1.0.0") but is flexible enough to handle any number of components separated by dots or other non-alphanumeric characters, pre-release tags (e.g., "1.0.0-alpha"), mixed components (e.g., "0ab"), and complex strings (e.g., "1.0ab.01-1"). This following explains what constitutes a valid version string and what does not, helping users effectively utilize the field.
What Can Be Used as a Version String¶
The Version key accepts version strings composed of components separated by any number of non-alphanumeric characters (e.g., dots, hyphens). Components can be numeric (e.g., "123"), alphabetic (e.g., "alpha"), or mixed (e.g., "123abc"). The function handles leading zeros, pre-release tags, letter suffixes, and any number of components (not limited to three dots). Below are the characteristics of valid version strings:
Numeric Versions with Any Number of Components:
- Strings like "1", "1.0", "1.0.0", "1.0.0.0", or "10.0.1.2.3".
- The number of dots (or other separators) is not restricted to three; you can have zero, one, two, three, four, or more components (e.g., "1.0" or "1.0.0.0.0").
- Numbers can include leading zeros, which are ignored during comparison (e.g., "01.00.00" is equivalent to "1.0.0").
- Components are separated by any non-alphanumeric characters (e.g., "1-0-0", "1..0--0", "1.0.0.0_0").
Pre-release Versions:
- Versions with alphabetic pre-release tags, such as "1.0.0-alpha", "2.0.0-beta", or "1.0.0.0-rc1".
- Pre-release tags (e.g., "alpha", "beta") are treated as higher precedence than stable versions (e.g., "1.0.0-alpha" < "1.0.0").
- Tags are case-insensitive (e.g., "1.0.0-ALPHA" is equivalent to "1.0.0-alpha").
- Only the first letter is taken into account whatever the length of the string.
Mixed Components:
- Components that combine numeric and alphabetic parts without a separator, such as "123abc" or "0ab", are valid.
- These are parsed as a numeric component followed by an alphabetic suffix:
- "123abc" splits into numeric "123" and alphabetic "abc".
- "0ab" splits into numeric "0" and alphabetic "ab".
- Example: "1.0.0-123abc" is parsed as numeric "123" followed by an alphabetic suffix "abc".
- Only the first letter is taken into account whatever the length of the string.
Letter Suffixes:
- Versions with an alphabetic suffix after a pre-release tag or mixed component, such as "1.0.0-alpha.1", "1.0.0-beta.patch", or "1.0ab.01-1".
- The alphabetic part is treated as a separate component with lower precedence than numeric components.
Complex Version Strings:
- Strings combining multiple component types with any number of separators, such as "1.0ab.01-1" or "1.0.0.0.0-alpha.2", are valid.
- Example breakdown of "1.0ab.01-1":
- "1": Numeric component.
- "0ab": Numeric "0" + alphabetic suffix "ab".
- "01": Numeric component (equivalent to "1").
- "1": Numeric component.
Shortened or Extended Versions:
- Versions with any number of components are valid, from a single component (e.g., "1") to many (e.g., "1.0.0.0.0").
- Shorter versions are treated as equivalent to versions padded with zeros (e.g., "1.0" is equivalent to "1.0.0", "1" is equivalent to "1.0.0.0").
- Extended versions with more components are compared component-by-component (e.g., "1.0.0.0" == "1.0.0").
Empty Strings:
- An empty string ("") is valid and treated as a version with a single zero component (equivalent to "0").
Separators:
- Any non-alphanumeric character (e.g., ., -, _, +) can act as a separator, and any number of consecutive separators is allowed and ignored (e.g., "1..0" is equivalent to "1.0", "1---0..0" is equivalent to "1.0.0").
- Separators are flexible, so "1-0-0", "1_0_0", and "1.0.0" are equivalent.
Examples of Valid Version Strings:
- "1"
- "1.0"
- "1.0.0"
- "1.0.0.0"
- "1.0.0.0.0"
- "2.0.0-alpha"
- "1.0.0-beta.1"
- "01.00.00"
- "1-0-0"
- "1.0.0-rc.2"
- "1.0ab.01-1"
- "1.0.0-123abc"
- ""
- "2.0.0--alpha..patch"
- "1-0ab-01--1"
- "10.0.1.2.3"
What Cannot Be Used as a Version String¶
While the Version key is robust, certain inputs are invalid or problematic. Users should avoid the following:
Special Characters in Components:
- Components should consist of numeric (0-9) or alphabetic (a-z, A-Z) characters. Special characters like @, #, or $ within components (not as separators) are not supported and may lead to incorrect parsing.
- Example: "1.0.0@alpha" is invalid because @alpha contains an unsupported character in the component.
Whitespace in Components:
- Whitespace within components (e.g., "1.0.0 alpha") is treated as a separator, which may split components unexpectedly. Use hyphens or dots for pre-release tags (e.g., "1.0.0-alpha").
- Example: "1.0.0 alpha" is valid from an algorithm point of view but the parsed will only consider the first element.
Excessively Long Strings: - Extremely long version strings (e.g., thousands of characters or hundreds of components) may cause performance issues or stack overflows due to the fixed-size arrays in the function. Keep version strings reasonably short (e.g., under 50 characters). - Example: A string with hundreds of components is technically valid but impractical.
Examples of Invalid or Problematic Version Strings:
- NULL (causes undefined behavior).
- "1.0.0@alpha" (invalid character @ in component).
- "1.0.0#patch" (invalid character # in component).
- "1.0.0 alpha" (whitespace splits components unexpectedly, likely not intended).
- A 51-character or higher string is invalid.
Appendix D — Full prototype¶
The minimal template is e.g.:
[Main]
Type = classic
[Start]
Execute = ( /usr/bin/true )
This prototype contains all valid sections with all valid key=value pairs.
[Main]
Type =
Description = ""
Version =
Depends = ()
RequiredBy = ()
OptsDepends = ()
Options = ()
Flags = ()
User = ()
CopyFrom = ()
InTree =
Provide = ()
Conflict = ()
EventType =
Watch =
On = ()
Expression = ""
Timezone =
Every =
[Start]
RunAs =
Execute = ()
Timeout =
Notify =
MaxDeath =
MaxDeathInterval =
[Stop]
RunAs =
Execute = ()
Timeout =
DownSignal =
[Logger]
RunAs =
Backup =
MaxSize =
Timestamp =
Timeout =
Execute = ()
[Environment]
ImportFile=/path/to/file
mykey=myvalue
ANOTHERKEY=!anothervalue
[Regex]
Configure = ""
Directories = ()
Files = ()
InFiles = ()
[Execute]
LimitAS =
LimitCORE =
LimitCPU =
LimitDATA =
LimitFSIZE =
LimitLOCKS =
LimitMEMLOCK =
LimitMSGQUEUE =
LimitNICE =
LimitNOFILE =
LimitNPROC =
LimitRTPRIO =
LimitRTTIME =
LimitSIGPENDING =
LimitSTACK =
BlockPrivileges =
UMask =
Nice =
ChangeDirectory = /directory/path
CapsBound = ()
CapsAmbient = ()
StdIn =
StdOut =
StdErr =
[Event]
EventType =
From = ()
On = ()
OnAll = ()
Do =
Emit =
Propagate =
The [Main] event keys (EventType, Watch, On, Expression, Timezone, Every) apply only to a Type = event source; the [Event] section applies only to a reactor. A frontend never holds both.