User Tools

This is an old revision of the document!


Custom User Services

Since i am running lots of various shaped and managed services, i needed an efficient and simple way to automatically start and stop them.

Gentoo already provides a good approach to starting and stopping services called SysVInit that can easily take care of all the services installed by the system, like the NGINX reverse proxy and similar stuff. In general, it can handle directly anything you have installed via emerge.

What about thos (lots of) services you are installing manually because they are not in portage or will never be in portage (ex: docker/podman based services…)? Gentoo give you a neat way to add your own scripts under /etc/local.d.

Let me show how i am managing this approach.

local.d

Local.d is the last service running in the SysVInit world just before the system is all ready. You can add your own custom startup scripts under the folder /etc/local.d with the following syntax:

10-myservice.start
10-myservice.stop

and, if those script are executable (+x), then will be called at startup and shutdown respectively.

See here for more details.

Indeed this is not considered officially a good place to do what i will show you should do, but YMMV and i find it a safer and less invasive way to start all our non-system services.

The Approach

You want to streamline deployment of services as much as possible, thus you do not want to manually manage tons of start/stop scripts.

One way would be to create your own init scripts and put them under /etc/init.d, which is mostly overkill for most of the services you will be installing. Moreover, i prefer to keep my system and my services as much separated as possible.

The other way is to use local.d service leveraging a specific script with symlinks: the idea is to use one script for most of the services, and just create a symlink to the start and stop scripts themselves.

Assuming you have a service called myservice, which is a podman container, run as myuser user, all you need to do it:

cd /etc/local.d
ln -s _servicer.sh 50-myservice-myuser-podman.start 
ln -s _servicer.sh 50-myservice-myuser-podman.stop

and that's it. This will automatically create the /var/run/myservice.pid file for service-monitoring, and allow proper start and stop of the service. This will require the usual docker-compose.yml file in the myuser folder.

Similarly, for a simple non-containerized service:

cd /etc/local.d
ln -s _servicer.sh 50-mynormalservice-myotheruser-service.start 
ln -s _servicer.sh 50-mynormalservice-myotheruser-service.stop

This will require a file called service_mynormalservice_start myotheruser folder, like this:

service_mynormalservice_start
COMMAND=/path/to/my/service/binary
ARGUMENTS=(my service arguments)

The Script

Leveraging great bash lore, here is the beast /etc/local.d/_servicer.sh:

_servicer.sh
#!/bin/bash
#
# Make a symlink to _servicer.sh with this syntax:
#  - XX-myservice-user-type.action
# 
# where:
#  - XX is a number (ex: 10)
#  - myservice will be the name of the service (it will create /var/run/myservice.pid for checking service status)
#  - user is the user the service will run as. Can be omitted, in this case use two "--", and user=service
#  - type is one of:
#        - podman: the service is a podman-compose based containeration
#        - script: the service is managed by a script ($user_home/myservice_start.sh or $user_home/myservice.sh, and $user_home/myservice_stop.sh (optional))
#        - service: the service command is specified inside a service_${service}_start file in the user home folder
#  - action is either start or stop
#
# 
 
service_user_type_action=${0#*-}
SERVICE=${service_user_type_action%%-*}
user_type_action=${service_user_type_action#*-}
USER=${user_type_action%-*}
test -z ${USER} && USER=${SERVICE}
type_action=${user_type_action#*-}
TYPE=${type_action%.*}
ACTION=${type_action#*.}
 
passwd_entry=$(getent passwd ${USER}) || exit 255
HOME=$(echo ${passwd_entry} | cut -d: -f 6)
 
if [ "${ACTION}" = "start" ]
then
        if [ "${TYPE}" = "podman" ]
        then
                COMMAND="$(which podman)"
                ARGUMENTS=(compose up)
                iptables -L -t nat &> /dev/null
                podman network create ${SERVICE}-net &> /dev/null
        elif [ "${TYPE}" = "script" ]
        then
                start_script="${HOME}/${SERVICE}_start.sh"
                test -e ${start_script} || start_script="${HOME}/${SERVICE}.sh"
                COMMAND="${HOME}/${SERVICE}.sh"
                ARGUMENTS=""
        elif [ "${TYPE}" = "service" ]
        then
                source_script="${HOME}/service_${SERVICE}_start"
                test -e "${source_script}" || {
                        echo "Error, missing '${source_script}'"
                        exit 255
                }
                source "${source_script}"
        fi
        action=(-b -m --start "${COMMAND}" -- ${ARGUMENTS[@]})
elif [ "${ACTION}" = "stop" ]
then
        if [ "${TYPE}" = "podman" ]
        then
                su - "${USER}" -c "$(which podman) compose down" &> /dev/null
                action=(--stop "${SERVICE}")
        elif [ "${TYPE}" = "script" ]
        then
                stop_script="${HOME}/${SERVICE}_stop.sh"
                test -e "${stop_script}" && su - ${USER} -c "${stop_script}"
        elif [ "${TYPE}" = "service" ]
        then
                true
        fi
        action=(--stop ${SERVICE})
fi
 
start-stop-daemon -p /var/run/${SERVICE}.pid -n ${SERVICE} -u ${USER} -d ${HOME} ${action[@]}

</file>

This website uses technical cookies only. No information is shared with anybody or used in any way but provide the website in your browser.

More information