Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
OpenRC init script best practices
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Portage & Programming
View previous topic :: View next topic  
Author Message
Zeault
n00b
n00b


Joined: 03 Aug 2019
Posts: 22
Location: New England, United States

PostPosted: Sun Mar 24, 2024 12:11 am    Post subject: OpenRC init script best practices Reply with quote

Hello,

I've been having a blast loading up my new-to-me home server with way too many daemons. I've noticed the quality/style/conventions of the OpenRC init scripts varies a lot between packages. I have rewritten or at least edited almost all of the scripts in my /etc/init.d/ directory and I was thinking of upstreaming some of my changes.

Before I do, I wanted to try and conform to the best-practices of OpenRC init scripts on Gentoo. However I have not really found any! There are plenty of articles and manuals describing the basics of init script writing, but I have found no centralized source of best practices that a developer can use to write an init script for their daemon in a way that will be robust and supported for a long time.

If this document does exist somewhere, please share!

If it doesn't exist, lets make it!

My intention in creating this thread is actually to start some discussion about what people think the best practices are. I'll start by listing some of the good practices that I've seen other people use in their init scripts, then I'll list some of my opinions and practices I've used. Finally, I have some questions I post to you about whether or not some things are good practice or not.

Good practices I've noticed
  • Init scripts should be written in POSIX-compatible shell language, because the script is executed by 'openrc-run' which is not guaranteed to be bash or any shell in particular. This means no [[ extended conditionals ]].
  • Instead of writing 'start()' and 'stop()' functions, it is better to define the variables 'command', command_args', ... and let openrc-run call its default start function. This is because OpenRC doesn't always use start-stop-daemon to launch the process. It can be configured with other daemon supervisors which won't work if start-stop-daemon is hardcoded into the init script.
  • If something needs to happen every time the daemon starts, it should be put in start_pre() or start_post(). stop_pre() and stop_post() also exist.
  • It is really nice if an init script has a checkconfig() function. If it exists, it should also be callable by the user with extra_commands='checkconfig'.
  • It also good if an init script has a callable reload() function but some daemons simply can't do this.

Some opinions I've developed (that I'm not sure everyone will agree with)
  • An init script should define sensible defaults so that it will work even if its corresponding conf.d entry is empty. This is easy with fallback parameter expansion:
    Code:
    : "${CONFIG:=/etc/daemon/config}"

  • The user should not be expected to define any of the special openrc-run variables in conf.d/*. I'm talking about the ones I mentioned earlier: command, command_args, and also ones like command_user, pidfile, ... all except maybe supervisor.
  • Speaking of pidfile, this variable should NOT be passed to the daemon. I've seen this a lot and I think this is a bug even though it works in the default configuration. If pidfile is defined, then the supervisor (start-stop-daemon by default) will spawn the process and save it's pid at pidfile variable. If this variable is passed to the daemon, then it will go and overwrite the pidfile with its pid again. This doesn't cause any problems in the default case, but if a different supervisor is configured then the pidfile might contain the pid of a watchdog process which will then be overwritten by the daemon.
  • If a daemon doesn't support syslog or doesn't do it in a sane way, it should have output_logger or error_logger defined.
  • Daemons should be started as a user instead of root if possible (controlled by the command_user variable). Some daemons need to bind to privileged ports or read privileged files, but this is better accomplished with other techniques like user/file capabilities, ACLs, or mandatory access control.

Some things I've put in my init scripts which might not be good... I dunno... you tell me
  • Usage of some commands that are common but may not necessarily be installed: grep, cut, awk, find, runuser, ... Maybe we ought to make a list of these that are considered OK.
  • Speaking of those commands, sometimes it is very convenient to define a variable as the result of a command. This allows settings to agree across different configuration files so they only need to be set once, but I'm not sure if this is really a good idea. I consider it a bit fragile since the script can break unexpectedly if the user moves the files. Also it seems to have some security implications. If you aren't sure what I'm talking about, here is an excerpt from my Caddy init script:
    Code:
    # Caddy is designed to be run as any user so it stores its
    # runtime state information in the home directory of the user it is run as
    STATE_DIR=$(grep "^${command_user%:*}:" '/etc/passwd' | cut -d : -f 6)

     ...

    # Run the config validation command with the home variables set.
    # Make sure it is run as the 'command_user' because the state dir
    # permissions are 0700
    HOME="${STATE_DIR}" \
    XDG_CONFIG_HOME="${STATE_DIR}/config" \
    XDG_DATA_HOME="${STATE_DIR}/data" \
    /usr/bin/runuser -m -u "${command_user#*:}" -- /usr/bin/caddy validate --config "${caddy_config}"


I thought I had a lot more of these but that's all I can think of right now.

Oh yeah one more thing: I've been writing AppArmor profiles for a lot of my daemons, and having all the init scripts written this way allows me to put this nice snippet in:
Code:
# Change the command and arguments if an AppArmor profile is defined
if [ -n "${AA_PROFILE}" ] ; then
        command_args="--profile=${AA_PROFILE} -- ${command} ${command_args}"
        command='/usr/bin/aa-exec'
fi


Thank you for reading. I welcome your commentary
Back to top
View user's profile Send private message
grknight
Retired Dev
Retired Dev


Joined: 20 Feb 2015
Posts: 1663

PostPosted: Sun Mar 24, 2024 12:20 am    Post subject: Reply with quote

Zeault wrote:
Instead of writing 'start()' and 'stop()' functions, it is better to define the variables 'command', command_args', ... and let openrc-run call its default start function. This is because OpenRC doesn't always use start-stop-daemon to launch the process. It can be configured with other daemon supervisors which won't work if start-stop-daemon is hardcoded into the init script.

Because of these supervisor differences, be sure to consider command_args closely. command_args should be common among all supervisors. command_args_background should be for options when the supervisor will fork the process, e.g. pidfile name to the daemon. command_args_foreground should be for options, if any, to keep the process in the foreground. supervise_daemon expects the (main) process to not fork (the process can fork workers just fine).
Back to top
View user's profile Send private message
pietinger
Moderator
Moderator


Joined: 17 Oct 2006
Posts: 4167
Location: Bavaria

PostPosted: Sun Mar 24, 2024 10:48 am    Post subject: Re: OpenRC init script best practices Reply with quote

Zeault wrote:
Some things I've put in my init scripts which might not be good... I dunno... you tell me
  • Usage of some commands that are common but may not necessarily be installed: grep, cut, awk, find, runuser, ... Maybe we ought to make a list of these that are considered OK.
    [...]

I can imagine that the use of programs installed by the system should be problem-free. You will get a list with:
Code:
eix -c --system

Check more with:
Code:
equery f coreutils

or in https://wiki.gentoo.org/wiki/GNU_Coreutils
and you can see that coreutils gives you "cut" and util-linux has "runuser".
_________________
https://wiki.gentoo.org/wiki/User:Pietinger
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Portage & Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum