Updated: Write and auto-deploy daemons on FreeBSD
Tags: devops, freebsd, daemon
Published: 2021-04-28 16:28:54

## Goal

Our goal is to write an rc.d file that allows us to start anything as a daemon, and an accompanying settings file, and to get to know the commands to use for automated deploy.

## Settings File

We will start with the settings file. *Technically* you can do in there whatever you want; if you have any sanity left, you will simply use it to set environment variables. This makes it easy to follow [the third rule of the Twelve-Factor App](https://12factor.net/config). And it really is that simple, you just add export statements, and make "dummy values" you will replace in your CI/CD script using sed, or replace them with variables and use `RENDER` in Bastille:

```bash
export HTTP_PORT=3000
export POSTGRES_IP=SED_PGSQL_IP
export POSTGRES_USERNAME=SED_PGSQL_USER
export POSTGRES_PASSWORD=SED_PGSQL_PASS
```

This file has to be stored with the same name as your actual service file, but under `/usr/local/etc/rc.conf.d/`. The RC system will auto-detect it.

## Daemon file

This one is going to be a little bit longer. Let's first take a look at the whole script:
```bash
#!/bin/sh

# PROVIDE: location_service
# REQUIRE: LOGIN DAEMON NETWORKING
# BEFORE: some_other_service
# KEYWORD: shutdown

. /etc/rc.subr

name=location_service
rcvar=${name}_enable
pidfile="/var/run/${name}.pid"
pidfile_child="/var/run/${name}_jvm.pid"
logfile="/var/log/${name}.log"
location_service_chdir="/usr/local/share/location-service"

command="/usr/sbin/daemon"
start_cmd="location_service_start"
procname="daemon"

load_rc_config ${name}
: ${location_service_enable:=no}

location_service_start() {
    /usr/sbin/daemon -r -f -P ${pidfile} -p ${pidfile_child} -t ${name} -o ${logfile} /usr/local/bin/java -jar location-service.jar
}

run_rc_command "$1"
```
### Main variables
There is some magic going on with the variable names, but that aside, it is a pretty standard shell script file. In this case, we are working with a location service that is to be run by the JVM, but it really doesn't matter - you can run literally anything as a service this way.

* `PROVIDE` is the name of the daemon; this is placed here again so potentially other daemons can REQUIRE it.
* `REQUIRE` are the daemons/targets that should be loaded before this one. The values should be quite self-explanatory.
* `BEFORE` is the inverse of REQUIRE; use it to tell the rc system to start this daemon before the mentioned one(s).
* `KEYWORD` can be either `nojail` (will be ignored if inside a jail), `nostart` (automatic startup procedure will ignore the script. In conjunction with the shutdown keyword, this can be used to write scripts that do something only at system shutdown), and `shutdown` (service needs to be stopped before system shutdown).
* `. /etc/rc.subr` loads subroutines that enable the "magic" to run the service.
* `rcvar` is the flag that you can set to enable or disable your service in the rc.conf file.
* The two pidfiles are holding the process id of the daemon and the actual process, so the system can keep track of it.
* and the `location_service_chdir` (or `whatever_you_name_it_chdir`) sets the working directory.

### The Daemon command
FreeBSD ships with a program aptly called daemon, and we are using it to run our normal program as one. We thus tell the rc system to use /usr/sbin/daemon as the main `command`, and tell it that the process started by it - `procname` - will be called "daemon". We also tell it to run a custom starting function, `start_cmd`, which in this case is called "location_service_start".

### Configure the [daemon](https://www.unix.com/man-page/freebsd/8/daemon/)
We configure the daemon by setting the flags when starting it. We have the following flags:
* `-r`: Restarts the process if it dies
* `-f`: Redirects standard input/output and standard error to /dev/null
* `-p`: the pidfile for the process you want to have daemonized
* `-P`: the pidfile of the daemon process itself - that is what the rc system needs
* `-o`: the log file to log to
* `-t`: gives the process a name (optional)
* `-u`: (not used here): the user to run the process as

This service file has to be stored under `/usr/local/etc/rc.d` and be made executable.

## Automatic deployment
We almost have it - the only thing missing are the commands for your CI/CD. It is really simple once you have both files at their respective locations.

You can set the config variables using `sed -i.bak "s/SED_PGSQL_IP/${your_ci_var}/g" /usr/local/etc/rc.conf.d/location_service`. Remember, if your variable contains the "/" character, you can use something else for sed, whatever character follows the "s" will be the separator.

Enable the service using `sysrc -f /etc/rc.conf "location_service_enable=YES"`.

And finally, don't forget to actually (re-)start it using `service location_service restart`.

Have fun!