Table of Contents

Currently viewing version: 0.8.2.1

The frontend service file

The s6 programs use different files. It is quite complex to understand and manage the relationship between all those files. If you're interested in the details you should read the documentation for the s6 servicedir and also about classic and module services. The frontend service file of 66 program allows you to deal with all these different services in a centralized manner and in one single location.

By default 66 program expects to find service files in /usr/share/66/service and /etc/66/service for root user, /usr/share/66/service/user and /etc/66/service/user for regular accounts. For regular accounts, $HOME/.66/service will take priority over the previous ones. Although this can be changed at compile time by passing the --with-system-service=DIR, --with-sysadmin-service=DIR and --with-user-service=DIR option to ./configure.

The frontend service file has a format of INI with a specific syntax on the key field. The name of the file usually corresponds to the name of the daemon and does not have any extension or prefix.

The file is made of sections which can contain one or more key = value pairs where the key name can contain special characters like - (hyphen) or _ (low line) except the character @ (commercial at) which is reserved.

You can find a prototype with all valid section and all valid key=value pair at the end of this document.

File names examples

/usr/share/66/service/dhcpcd
/usr/share/66/service/very_long_name_which_make_no_sense

File content example

[Main]
Type = classic
Description = "ntpd daemon"
Version = 0.1.0
User = ( root )

[Start]
Execute = (
    foreground { mkdir -p  -m 0755 ${RUNDIR} }
    execl-cmdline -s { ntpd ${CMD_ARGS} }
)

[Environment]
RUNDIR=!/run/openntpd
CMD_ARGS=!-d -s

The parser will not accept an empty value. If a key is set then the value can not be empty. Comments are allowed using the number sign #. Empty lines are also allowed.

Key names are case sensitive and can not be modified. Most names should be specific enough to avoid confusion.

Sections

All sections need to be declared with the name written between square brackets [] and must begins with a uppercase followed by lowercase letters only. This means that special characters and numbers are not allowed in the name of a section.

The [Main] section must be declared first.

The frontend service file allows the following section names:

Although a section can be mandatory not all of its key fields must be necessarily so.

Syntax legend

The value of a key is parsed in a specific format depending on the key. The following is a break down of how to write these syntaxes.

inline

An inline value. Must be on the same line with its corresponding key.

A value between double-quotes. Must be on the same line with its corresponding key.

brackets

Multiple values between parentheses (). Values need to be separated with a space. A line break can be used instead.

uint

A positive whole number. Must be on the same line with its corresponding key.

path

An absolute path beginning with a forward slash /. Must be on the same line with its corresponding key.

pair

Same as inline.

colon

A value between double colons followed by a pair syntax. Must be one by line.

simple-colon

A values separated by a colon. Must be on the same line with its corresponding key.

boolean

A value specifying a true state for the key. Must be on the same line with its corresponding key. If the key is not defined, it defaults to false.

Section [Main]

This section is mandatory. (!)

Type

Source Snippet:

Type = classic

Defines the service type. Determines how 66 orchestrates startup and supervision.

Version

Source Snippet:

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.

Description

Source Snippet:

Description = "ntpd daemon"

Provides a concise, human-readable summary of the service’s purpose. Enclosed in double quotes. If not specified, defaults to " service".

User

Source Snippet:

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

Depends

Source Snippet:

Depends = ( fooA fooB fooC )

Declares the mandatory service dependencies. Each listed service must start successfully before this service launches.

RequiredBy

Source Snippet:

RequiredBy = ( fooX fooY )

Specifies reverse dependencies—services that depend on this service. Starting or enabling this service automatically updates those listed.

OptsDepends

Source Snippet:

OptsDepends = ( fooA fooB )

Lists optional dependencies. 66 will enable the first available service from this list at startup.

Options

Source Snippet:

Options = (log)

Configures optional behaviors for the service. Wrap options in parentheses for multiple entries.

Flags

Source Snippet:

Flags = (down earlier)

Notify

Source Snippet:

Notify = 3

Enables readiness notification. Creates notification-fd containing the specified file descriptor number.

TimeoutStop

Source Snippet:

TimeoutStop = 5000

Specifies the maximum time (ms) to wait for the stop script (finish) to complete before forcibly killing it.

TimeoutStart

Source Snippet:

TimeoutStart = 2000
Defines the grace period (ms) after SIGTERM before sending SIGKILL on stop commands.

MaxDeath

Source Snippet:

MaxDeath = 10

Limits the number of recorded service crashes. Exceeding this resets the oldest record when tallying failures.

DownSignal

Source Snippet:

DownSignal = SIGTERM

Specifies which signal to send when stopping or reloading the service.

CopyFrom

Source Snippet:

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 copy it to the root of the service directory.

InTree

Source Snippet:

InTree = my-tree

Automatically activate the service within a named service tree. If a corresponding seed file exists, it will be applied.

StdIn

Source Snippet:

StdIn = null

Controls standard I/O redirection for the standard input entries.

StdOut

Source Snippet:

StdOut = s6log

Controls standard I/O redirection for the standard output entries.

StdErr

Source Snippet:

StdErr = inherit

Controls standard I/O redirection for the standard error entries.

Provide

Source Snippet:

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.

Conflict

Source Snippet:

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.

Section [Start]

This section is mandatory. (!)

Build

Source Snippet:

Build = auto

Determines how the service script is generated from the Execute field.

RunAs

Source Snippet:

RunAs = oblive
Drops privileges to the specified user or UID:GID before executing the service.

Execute

Source Snippet:

Execute = ( /usr/bin/auditd -f )
Defines the command(s) executed to start the service. Enclose multiple lines in brackets.

Section [Stop]

This section is optional.

This section is exactly the same as Start and shares the same keys. With the exception that it will handle the stop process of the service.

Section [Logger]

This section is optional and controls the behavior of the default logging system used by 66, which utilizes the excellent s6-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 Main must be set to s6log, or these keys must not be defined at all.

This section extends the Build, RunAs, and Execute key fields from Start and the TimeoutStop and TimeoutStart key fields from Main . These are also valid keys for Logger and behave the same way they do in the other sections but they can not be specified except for the mandatory key Build—see example below. In such case the default behaviour for those key are apply.

Furthermore there are some keys specific to the log.

The following key names are also valid:

Backup

Source Snippet:

Backup = 3
Number of rotated log files to retain before overwriting the oldest.

MaxSize

Source Snippet:

MaxSize = 1000000
Byte threshold to trigger log rotation when the current file grows too large.

Timestamp

Source Snippet:

Timestamp = iso

Specifies timestamp format prefixed to each log entry.

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 --with-sysadmin-service-conf=DIR option to ./configure.

Any key=value pair

Source Snippet:

DirRun=/run/openntpd

ImportFile

Source Snippet:

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.

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.

Configure

Source Snippet:

Configure = "--enable-feature"

Arguments passed to the module’s configure script.

Directories

Source Snippet:

Directories = ( DM=sddm )

Regex-based renaming rules for module subdirectories. Each entry is regex=replacement.

Files

Source Snippet:

Directories = ( servicename=newname )

Regex-based renaming rules for files. Each entry is regex=replacement.

InFiles

Source Snippet:

InFiles = ( :mount-tmp:args=-o noexec )

In-file regex replacements for module files. Use :filename:regex=replacement or ::regex=replacement for all files.

Section [Execute]

This section is optional.

This section configures tasks executed just before calling exec for the service’s start and stop processes.

Resource limits (e.g., LimitNICE, LimitAS) sets soft (rlim_cur) and hard (rlim_max) limits by retrieving current limits with getrlimit(), adjusting the hard limit for root-owned services if needed, capping the soft limit to the hard limit for non-root services, and applying the new limits with setrlimit(). If a limit is zero, no changes are made.

All LimitXXX keys accept the value unlimited to set the corresponding RLIMIT_* resource to RLIM_INFINITY, allowing unrestricted use of that resource. For unprivileged services (non-root users), unlimited or values exceeding the current hard limit (rlim_max) are capped at rlim_max to prevent errors. Setting unlimited or raising limits beyond rlim_max requires root privileges or CAP_SYS_RESOURCE. Linux-specific limits (e.g., LimitNICE, LimitRTPRIO) are ignored on systems where they are not supported.

LimitAS

Source Snippet:

LimitAS = unlimited

Specifies the maximum address space (virtual memory) for the service process, in bytes. Corresponds to the RLIMIT_AS resource limit.

LimitCORE

Source Snippet:

LimitCORE = 0

Specifies the maximum size of core dump files generated by the service process, in bytes. Corresponds to the RLIMIT_CORE resource limit.

LimitCPU

Source Snippet:

LimitCPU = unlimited

Specifies the maximum CPU time the service process can use, in seconds. Corresponds to the RLIMIT_CPU resource limit.

LimitDATA

Source Snippet:

LimitDATA = 5242880

Specifies the maximum size of the service process’s data segment, in bytes. Corresponds to the RLIMIT_DATA resource limit.

LimitFSIZE

Source Snippet:

LimitFSIZE = unlimited

Specifies the maximum size of files the service process can create, in bytes. Corresponds to the RLIMIT_FSIZE resource limit.

LimitLOCKS

Source Snippet:

LimitLOCKS = 1024

Specifies the maximum number of file locks the service process can hold. Corresponds to the RLIMIT_LOCKS resource limit (Linux-specific).

LimitMEMLOCK

Source Snippet:

LimitMEMLOCK = 65536

Specifies the maximum amount of memory the service process can lock into RAM, in bytes. Corresponds to the RLIMIT_MEMLOCK resource limit.

LimitMSGQUEUE

Source Snippet:

LimitMSGQUEUE = 819200

Specifies the maximum size of POSIX message queues the service process can create, in bytes. Corresponds to the RLIMIT_MSGQUEUE resource limit (Linux-specific).

LimitNICE

Source Snippet:

LimitNICE = -20

Specifies the maximum nice value (scheduling priority) the service process can set. Corresponds to the RLIMIT_NICE resource limit (Linux-specific).

LimitNOFILE

Source Snippet:

LimitNOFILE = 1024

Specifies the maximum number of open file descriptors the service process can have. Corresponds to the RLIMIT_NOFILE resource limit.

LimitNPROC

Source Snippet:

LimitNPROC = 4096

Specifies the maximum number of processes the service process’s user can create. Corresponds to the RLIMIT_NPROC resource limit.

LimitRTPRIO

Source Snippet:

LimitRTPRIO = 50

Specifies the maximum real-time priority the service process can set. Corresponds to the RLIMIT_RTPRIO resource limit (Linux-specific).

LimitRTTIME

Source Snippet:

LimitRTTIME = unlimited

Specifies the maximum real-time CPU time the service process can use, in microseconds. Corresponds to the RLIMIT_RTTIME resource limit (Linux-specific).

LimitSIGPENDING

Source Snippet:

LimitSIGPENDING = 8192

Specifies the maximum number of queued signals the service process can have. Corresponds to the RLIMIT_SIGPENDING resource limit (Linux-specific).

LimitSTACK

Source Snippet:

LimitSTACK = 8388608

Specifies the maximum stack size for the service process, in bytes. Corresponds to the RLIMIT_STACK resource limit.

BlockPrivileges

Source Snippet:

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).

UMask

Source Snippet:

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.

Nice

Source Snippet:

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.

ChangeDirectory

Source Snippet:

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).

CapsBound

Source Snippet:

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.

CapsAmbient

Source Snippet:

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.

A word about the Execute key

As described above the Execute key can be written in any language as long as you define the key Build as custom. For example if you want to write your Execute field with bash:

Build = custom
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.

Note that with Build=custom, 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.

A word about the Version key

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 le lenght 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 le lenght 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.

Prototype of a frontend file

The minimal template is e.g.:

[Main]
Type = classic

[Start]
Execute = ( /usr/bin/true )

This prototype contain all valid section with all valid key=value pair.

[Main]
Type =
Description = ""
Version =
Depends = ()
RequiredBy = ()
OptsDepends = ()
Options = ()
Flags = ()
Notify =
User = ()
TimeoutStart =
TimeoutStop =
MaxDeath =
DownSignal =
CopyFrom = ()
InTree =
StdIn =
StdOut =
StdErr =
Provide = ()

[Start]
Build =
RunAs =
Execute = ()

[Stop]
Build =
RunAs =
Execute = ()

[Logger]
Build =
RunAs =
Destination =
Backup =
MaxSize =
Timestamp =
TimeoutStart =
TimeoutStop =
Execute = ()

[Environment]
ImportFile=/path/to/file
mykey=myvalue
ANOTHERKEY=!antohervalue

[Regex]
Configure = ""
Directories = ()
Files = ()
InFiles = ()

[Execute]
LimitAS =
LimitCORE =
LimitCPU =
LimitDATA =
LimitFSIZE =
LimitLOCKS =
LimitMEMLOCK =
LimitMSGQUEUE =
LimitNICE =
LimitNOFILE =
LimitNPROC =
LimitRTPRIO =
LimitRTTIME =
LimitSIGPENDING =
LimitSTACK =
BlockPrivileges =
UMask =
ChangeDirectory = /directory/path
CapsBound = ()
CapsAmbient = ()