2

I have recently created my first custom systemd service to run a script early in my machine's boot sequence. The custom .service file is being copied to /etc/systemd/system, which I understand to be the correct location for custom services which are not deployed via packages or part of the operating system distribution.

It's a oneshot type service, which invokes a shell script to dynamically set the hostname, prior to the networking stack and dhcpcd being started. Here's the service definition:

[Unit]
Description=Set hostname on startup, based on hardware serial number
Wants=local-fs.target
After=local-fs.target
Before=systemd-hostnamed.service network.target

[Service]
Type=oneshot
ExecStart=/SOMEPATH/hostname-from-serialnumber.sh

[Install]
WantedBy=systemd-hostnamed.service network.target

I'm not sure what the best location for the shell script is, so I've put a placeholder SOMEPATH in the code block above. What is the correct location for this shell script, and why?

1
  • I found that the Before=systemd-hostnamed.service constraint was not sufficient to ensure that the hostname was updated before the system first requested an IP address via DHCP. So, I've added network.target to the Before= and WantedBy= lists. dhcpcd.service depends on network.target. Commented Feb 23, 2022 at 3:33

2 Answers 2

4

You are exactly right about /etc/systemd/system/*.service being an appropriate place for your custom service file.

As for your script, put it in /usr/local/... instead of /usr/.... That's because this script is written by you. Here are some reasons why this convention exists:

  • Files in /usr/{bin,lib,share}/ are owned by the package manager. You should be able to dpkg -S <file>, pacman -Qs <file> or rpm -q --whatprovides <file> on any file out which package owns it.
  • You can remember what files are "yours" (i.e. if you rebuild this machine, it's easy to see what you need to copy over or what you can simply get from a package)
  • If you install a package, a generic-named script will not be overwritten by the package.

Now, you need to choose the next directory. Options are:

  • /usr/local/bin: is appropriate if your script is executable (e.g., compiled binary or uses a shebang like #!/bin/bash or #!/usr/bin/python3) and can be run by any user. I tend to use this a lot because it is quite simple.
  • /usr/local/sbin: This is appropriate if your script meets the needs of /usr/local/bin, but shouldn't be run by users. Instead it should be run by a system administrator only. /usr/local/sbin is often only included in the $PATH of a sudo environment.
  • /usr/local/lib: On some systems (e.g. Debian-based systems), this directory may include internal binaries that are not intended to be executed directly by users or shell scripts. However on other systems (i.e. Redhat-based systems), lib only includes object files and libraries.
  • /usr/local/libexec: This directory includes internal binaries that are not intended to be executed directly by users or shell scripts. This is only supported by some distributions.
  • /usr/local/share/<ServiceName>/. This only applies if your script is not executable and is architecture independent (i.e. not compiled). You have a *.sh script, but if there is no shebang and it is not executable, then you'll need to run it by calling the interpreter and passing the path to this script as an argument. /bin/bash /path/to/script.sh. In this case, the script could be considered a read-only architecture independent data file which should go in /usr/local/share. It's a good idea to put it down one further subdirectory to avoid cluttering /usr/local/share. Any documentation you choose to write for your service can also go in share.
1

Hmmm...this is a tricky question. Assuming that this is non-OS code that wasn't part of a distribution, normally I would say it needs to go in /usr/local/lib/system and be linked to from /etc/systemd/system. The problem with that is that if all the filesystems aren't mounted when the system is coming up, it might cause problems. So, if you're feeling lucky you could try to name it something unlikely to be overwritten by an upgrade and place it in the same directory that contains most of them: /lib/systemd/system with an appropriate link. IMHO.

2
  • 1
    My service has Wants=local-fs.target and After=local-fs.target in its definition, so I think that should ensure that the local filesystems are mounted ok by the time systemd tries to invoke the script. Commented Feb 18, 2022 at 2:13
  • 1
    I've tested it with the shell script in /usr/local/lib and the mounting seems to work fine. Commented Feb 18, 2022 at 4:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .