The s6 and s6-rc programs each handle and use several kinds of services and different files. It is quite complex to understand and manage the relationship between all those files and services. If you're interested in the details you should read the documentation for the s6 servicedir and also about classic, oneshot, longrun (also called atomic services) and bundle services on Obarun. The frontend service file of 66 tools allows you to deal with all these different services in a centralized manner in one single place.
By default 66 tools expects to find any service files in %%service_system%% although this can be changed at compile time by passing the --with-service-path=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.
/etc/66/service/dhcpcd
/etc/66/service/very_long_name
[main] @type = classic @description = "ntpd daemon" @user = ( root ) @options = ( log env ) [start] @build = auto @execute = ( foreground { mkdir -p -m 0755 ${RUNDIR} } execl-cmdline -s { ntpd ${CMD_ARGS} } ) [environment] dir_run=!/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.
The sections can be declared in any order but as a good practice the [main] section should be declared first. That way the parser can read the file as fast as possible.
All sections need to be declared with the name written between square brackets '[]' and must be of lowercase letters only. This means that special characters, uppercase letters and numbers are not allowed in the name of a section. An entire section can be commented out by placing the number sign '#' in front of the opening square bracket like this:
#[stop]
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.
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:
Valid syntax:
@type = classic
@type=classic
(!) Invalid syntax:
@type=
classic
Valid syntax:
@description = " some awesome description "
@description="some awesome description"(!) Invalid syntax:
@description = "line break inside a double-quote
is not allowed"
Valid syntax:
@depends = ( fooA fooB fooC )
@depends=(fooA fooB fooC)
@depends=( fooA fooB fooC )
@depends= ( fooA fooB fooC )
(!) Invalid syntax:
@depends = (fooAfooBfooC)
Valid syntax:
@notify = 3
@notify=3
(!) Invalid syntax:
@notify= 3
Valid syntax:
@destination = /etc/66
@destination=/etc/66
(!) Invalid syntax:
@destination=/a/very/ long/path
Valid syntax:
MYKEY = MYVALUE
anotherkey=anothervalue
anotherkey=where_value=/can_contain/equal/Character
(!) Invalid syntax:
MYKEY= MYVALUE
This section is mandatory. (!)
Declare the type of the service.
mandatory : yes (!)
syntax : inline
valid values :
Note: If you don't care about dependencies between services or if you don't need specific tasks to get done before running the daemon, "classic" is the best pick.
Name of the service.
mandatory : no
syntax : inline
valid values :
For example, the following is valid:
@name = tty@mine-@I
where:
@name = mine-@I
is not for a frontend service file named tty@.
A short description of the service.
mandatory : yes (!)
syntax : quote
valid values :
Declare the permissions of the service.
mandatory : yes (!)
syntax : bracket
valid values :
Declare dependencies of the service.
mandatory : no—this field has no effect if the type of the service is classic.
syntax : bracket
valid values :
@depends=(fooA fooB fooC)It is unnecessary to manually define chained sets of dependencies. The parser will properly handle this for you. For example, if fooA depends on fooB—no matter what the underlying implementation of fooB is, and although the current implementation of fooB depends on fooC—you should just put fooB in the @depends key field of fooA. When starting the set, 66-enable will parse and enable fooA, fooB and fooC and 66-start will start fooC first, then fooB, then fooA. If the underlying implementation of fooB changes at any moment and does not depend on fooC anymore, you will just have to modify the @depends field for fooB. Beware though that if fooA depends on fooC, you need to add both fooB and fooC to the dependencies of fooA.
Declare optional dependencies of the service.
mandatory : no—this field has no effect if the type of the service is classic or bundle.
syntax : bracket
valid values :
The order is important (!). The first service found will be used and the parse process of the field will be stopped. So, you can considere @optsdepends field as: "enable one on this service or none".
@optsdepends only affects the enable process. The start process will not consider optional dependencies. If fooA on treeA has an optional dependency fooB which is declared on treeB, it's the responsibility of the sysadmin to start first treeB then treeA. 66-intree can give you the start order with the field Start after.
Declare external dependencies of the service.
mandatory : no—this field has no effect if the type of the service is classic or bundle.
syntax : bracket
valid values :
So, you can consider @extdepends field as: "enable the service if it is not already declared on a tree".
@extdepends only affects the enable process. The start process will not consider external dependencies. If fooA on treeA has an external dependency fooB which is declared on treeB, it's the responsibility of the sysadmin to start first treeB then treeA. 66-intree will give you the start order with the field Start after.
Declare the contents of a bundle service.
mandatory : yes (!)—for services of type bundle. Optional for services of type oneshot or longrun. No effect at all for services of type classic.
syntax : bracket
valid values :
mandatory : no
syntax : bracket
valid values :
Note: The funnel feature of pipelining is not implemented yet.
mandatory : no
syntax : bracket
valid values :
This will create the file nosetsid
Once this file was created the service will be run in the same process group as the supervisor of the service (s6-supervise). Without this file the service will have its own process group and is started as a session leader.
mandatory : no
syntax : uint
valid values :
This will create the file notification-fd. Once this file was created the service supports readiness notification. The value equals the number of the file descriptor that the service writes its readiness notification to. (For instance, it should be 1 if the daemon is s6-ipcserverd run with the -1 option.) When the service is started or restarted and this file is present and contains a valid descriptor number, 66-start will wait for the notification from the service and broadcast readiness, i.e. any 66-svctl -U process will be triggered.
mandatory : no
syntax : uint
valid values :
This will create the file timeout-finish. Once this file was created the value will equal the number of milliseconds after which the ./finish script—if it exists—will be killed with a SIGKILL. The default is 5000; finish scripts are killed if they're still alive after 5 seconds. A value of 0 allows finish scripts to run forever.
mandatory : no
syntax : uint
valid values :
This will create the file timeout-kill. Once this file was created and the value is not 0, then on reception of a 66-stop command—which sends a SIGTERM and a SIGCONT to the service—a timeout of value milliseconds is set. If the service is still not dead after value milliseconds it will receive a SIGKILL. If the file does not exist, or contains 0 or an invalid value, then the service is never forcibly killed (unless, of course, a s6-svc -k command is sent).
mandatory : no
syntax : uint
valid value :
This will create the file timeout-up. Once this file was created the value will equal the maximum number of milliseconds that 66-start will wait for successful completion of the service start. If starting the service takes longer than this value, 66-start will declare the transition a failure. If the value is 0, no timeout is defined and 66-start will wait for the service to start until the maxdeath is reached. Without this file a value of 3000 (3 seconds) will be taken by default.
mandatory : no
syntax : uint
valid value :
This will create the file timeout-down. Once this file was created the value will equal the maximum number of milliseconds 66-stop will wait for successful completion of the service stop. If starting the service takes longer than this value, 66-stop will declare the transition a failure. If the value is 0, no timeout is defined and 66-stop will wait for the service to start until the maxdeath is reached. Without this file a value of 3000 (3 seconds) will be taken by default.
mandatory : no
syntax : uint
valid value :
This will create the file max-death-tally. Once this file was created the value will equal the maximum number of service death events that the supervisor will keep track of. If the service dies more than this number of times, the oldest event will be forgotten and the transition (66-start or 66-stop) will be declared as failed. Tracking death events is useful, for example, when throttling service restarts. The value cannot be greater than 4096. Without this file a default of 3 is used.
mandatory : no
syntax : uint
valid value :
This will create the file down-signal which is used to kill the supervised process when a 66-start -r or 66-stop command is used. If the file does not exist SIGTERM will be used by default.
Copy subdirectories and files on the fly to the main service destination.
mandatory : no
syntax : bracket
valid values :
@hiercopy=(data scripts)to copy these to the service directory destination.
This section is mandatory. (!)
mandatory : yes (!)
syntax : inline
valid value :
The corresponding file to start the service will automatically be written in execline format with the @execute key value.
The corresponding file to start the service will be written in the language set in the @shebang key value.
mandatory : no
syntax : inline
valid value :
This will pass the privileges of the service to the given user before starting the last command of the service.
Note: (!) The service needs to be first started with root if you want to hand over priviliges to a user. Only root can pass on privileges. This field has no effect for other use cases.
mandatory : yes (!)—if the @build key is set to custom.
syntax : quotes, path
valid value :
This will set the language that will be used to read and write the @execute key value.
mandatory : yes (!)
syntax : bracket
valid value :
Note: The field will be used as is. No changes will be applied at all. It's the responsability of the author to make sure that the content of this field is correct.
This section is optional.
This section is exactly the same as [start] and shares the same keys. With the exception that it will only be considered when creating the file "finish" for a classic or longrun service and when creating the file "down" for a oneshot service to create its content.
This section is optional.
The value log must be added to the @options key in the [main] section for [logger] to have any effect.
This section extends the @build, @runas, @shebang and @execute key fields from [start]/[stop] and the @timeout-finish and @timeout-kill 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.
mandatory : no
syntax : path
valid value :
The directory where to save the log file. This directory is automatically created. The current user of the process needs to have sufficient permissions on the destination directory to be able to create it. The default directory is /var/log/66/servicename for root and $HOME/.66/log/servicename for any regular user. The default can also be changed at compile-time by passing the --with-system-logpath=DIR option for root and --with-user-logpath=DIR for a user to ./configure.
mandatory : no
syntax : uint
valid value :
The log directory will keep value files. The next log to be saved will replace the oldest file present. By default 3 files are kept.
mandatory : no
syntax : uint
valid value :
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 than 4096 or higher than 268435455.
mandatory : no
syntax : inline
valid value :
The logged line will be preceded by a TAI64N timestamp (and a space) before being processed by the next action directive.
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.
The following are two possible examples for the [logger] section definition.
[logger] @build = auto @runas = user @timeout-finish = 10000 @destination = /run/log @backup = 10 @timestamp = iso
[logger] @build = auto @timestamp = iso
This section is optional.
It will only have an effect when the value env is added to the @options key in the [main] section.
A file named key with the value as contain will be created by default at %%service_admconf%%/name_of_service directory. The default can also be changed at compile-time by passing the --with-sysadmin-service-conf=DIR option to ./configure.
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 -s
The '!' character can be put in front of the value like this :
[environment] dir_run=!/run/openntpd cmd_args=!-d -s
This will explicitly not set the value of the key for the runtime process but only at the start of the service. In this example the key=value pair passed to the command line does not need to be present in the general environment variable of the service.
As described above the @execute key can be written in any language as long as you define the key @build as custom and the @shebang key to the language interpreter to use. For example if you want to write your @execute field with bash :
@build = custom @shebang = "/usr/bin/bash" @execute = ( echo "This script displays available services" for i in $(ls %%service_system%%); do echo "daemon : ${i} is available" done )This is an unnecessary example but it shows how to construct this use case. The parser will put your @shebang at the beginning of the script and copy the contents of the @execute field. So, the resulting file will be :
#!/usr/bin/bash echo "This script displays available services" for i in $(ls %%service_system%%); do echo "daemon : ${i} is available" doneWhen using this sort of custom function @runas has no effect. You must define with care what you want to happen in a custom case.
Furthermore when you set @build to auto the parser will take care about the redirection of the ouput of the service when the logger is activated. When setting @build to custom though the parser will not do this automatically. You need to explicitly tell it to:
#!/usr/bin/bash exec 2>&1 echo "This script redirects file descriptor 2 to the file descriptor 1" echo "Then the logger reads the file descriptor 1 and you have" echo "the error of the daemon written into the appropriate file"
Moreover, for oneshot type the @shebang needs to contain the interpreter options as below:
@build = custom @shebang = "/usr/bin/bash -c" @execute = ( echo "this is a oneshot service with a correct shebang definition" )
Finally you need to take care about how you define your environment variable in the section [environment]. When setting @build to auto the parser will also take care about the '!' character if you use it. This character will have no effect in the case of custom.
This same behavior applies to the [logger] section. The fields @destination, @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.
An instance service file is of the same syntax as decribed in this document for any other service. It can be any type of service. However some differences exist :
Example :
File name : tty@ Contents : [main] @type = classic @description = "Launch @I" @user = ( root ) [start] @build = auto @execute = ( agetty -J 38400 @I } )
By using 66-enable tty@tty1, the resulting file will then be:
[main] @type = classic @description = "Launch tty1" @user = ( root ) [start] @build = auto @execute = ( agetty -J 38400 tty1 } )
This prototype contain all valid section with all valid key=value pair.
[main] @type = classic,bundle,longrun,oneshot @name = @description = "" @depends = () @optsdepends = () @extdepends = () @contents = () @options = ( log env pipeline ) @flags = ( down nosetsid ) @notify = @user = () @timeout-finish = @timeout-kill = @timeout-up = @timeout-down = @maxdeath = @down-signal = @hiercopy = () [start] @build = auto,custom @runas = @shebang = "/path" @execute = () [stop] @build = auto,custom @runas = @shebang = "/path" @execute = () [logger] @build = auto,custom @runas = @shebang = "/path" @destination = /path @backup = @maxsize = @timestamp = @timeout-finish = @timeout-kill = @execute = () [environment] MYKEY=myvalue ANOTHERKEY=!antohervalue