17

I need to run systemd-tmpfiles --create during the boot process with a systemd distro. So I need to create a systemd .service file doing this job.

In this question you can read all the details about what I need and why: How does systemd-tmpfiles work?

I have read some docs about it and I am writing the following test:

[Unit]
Description=Execute tmpfiles to disable usb-wakeup # see details in the link above
Requires=multi-user.target # see details in the link above
After=multi-user.target    # see details in the link above

[Service]
Type=oneshot
ExecStart=/usr/bin/systemd-tmpfiles --create

[Install]
WantedBy=multi-user.target

But I'm not sure, because systemd-tmpfiles is not a simple program but a piece of systemd itself. I wouldn't like to break my system.

Any tips about a correct .service file?

1

2 Answers 2

32

[This does not directly address the issue of systemd-tmpfiles but I think you have already recognized that in this particular case you are better off just using echo.]

First up, "multi-user.target" may or may not be what you want to use. If you are familiar with the concept of runlevels from SysV style init stuff, multi-user is the systemd equivalent of runlevel 3, which is a multi-user system that boots to a console, not a GUI. The equivalent of runlevel 5, which boots to X, is graphical.target. The default is determined by a symlink in /etc/systemd/system (and/or /lib/systemd/system; the one in /etc will overrule the one in /lib) called default.target, use ls to find where it points:

»ls -l /etc/systemd/system/default.target
default.target -> /usr/lib/systemd/system/multi-user.target

systemctl get-default will tell you "multi-user.target" in this case. For normal linux desktops it will be graphical.target. This is actually not important if you want the boot service you are creating to start regardless of what the default runlevel/target is -- in that case, we can just use default.target, and not worry what it is an alias for. If you use multi-user, however, and your default is graphical, your service won't happen.

Depending on the service, there may be more appropriate and specific targets or services that you want to start this one in relation to. Based on your other question, default.target is probably fine. As a note, the difference between a "target" and a "service" is that a service contains a [Service] section which actually runs a process; a target is just a way of grouping services together via the various "depends" and "requires" directives; it doesn't do anything of its own beyond triggering other targets or services.

When a service starts is determined by what other services explicitly depend on it. In the case of a simple, stand-alone event like this that we want run late in the boot process, we can use this combination of directives:

[Unit]
Requires=local-fs.target
After=local-fs.target 

[Install]
WantedBy=default.target

The "Install" section is used when the service is installed. "WantedBy=" specifies a target we want this service to be included with, meaning it will run if that target does. If you don't have specific dependencies, getting the unit to run later rather than sooner may be a matter of looking at what's going on normally and picking something to use as a dependency or an optional prerequisite.

To distinguish: By dependency I mean something which your unit requires to also be activated, and by optional prerequisite I mean something that should run before your unit if it is being used, but it is not required. Those terms are mine, but this is an important distinction used in the systemd documentation, particularly in the sense that a required dependency is guaranteed to be started if your unit is, but this requirement does not influence the order in which they are started, meaning, something that is just a dependency may actually be started afterwards (and yes, since that means your unit may be started first, the dependency is not guaranteed to succeed).

Above, Requires on local-fs.target may be a bit pointless unless you think your unit is going to be used on a system where it might not be included otherwise, but combining it with After means your unit is guaranteed to be started after it is -- so you could do without the Requires (you can set a unit to start after a unit that it doesn't depend on, hence "After" without "Requires" = an optional prerequisite). The example here is just to introduce the concepts and the distinction between dependency and order of execution: One does not determine the other.

Note that "started after" still doesn't mean the prereq will have reached any particular point it its own execution. Eg., if it is about mounting remote filesystems and you this is important to your unit, you will want to use Requires and probably After the service that establishes that but you still need the actual process you are executing to do proper error handling in case the remote filesystems are not yet accessible (eg., by sleeping in a loop until they are).

For the example, I'll just echo "hello world" to the console. The service itself is described in the [Service] section:

[Service]
Type=simple
ExecStart=/usr/local/bin/helloworld

The command needs a full path. The reason I did not just use /usr/bin/echo "hello world" is that it won't work (the output goes to /dev/null, I think), and while a service that does an echo "hello world" > /dev/console will, experimentation demonstrates that using shell redirection in an ExecStart directive won't, because the ExecStart command isn't run by a shell. But you can make so: /usr/local/bin/helloworld is a shell script with that one line, echo "hello world" > /dev/console.1

Note the Type=simple. This is fine for what helloworld does, and a great many other things. If your service is long running (beyond a few seconds), systemd will fork it to the background when using simple, which is what you want (the other option is to have it killed for remaining in the foreground too long). However, if the program does this fork itself (as servers and daemons often do) you should use Type=forking. Under simple, it will be killed as a stray orphan process. The "Type" param is covered in detail in man systemd.service and you should read that part regardless of what you are trying to do.2

Our complete, minimal service file is just those three sections ([Unit], [Service], and [Install]). To install, place the file or a symlink to it in either /etc/systemd/system or /usr/lib/systemd/system, and:

systemctl --system enable helloworld

It should print ln -s .... This does not run the service, it just configures it to run at boot as discussed above.

That's it in a nutshell. man systemd.unit and man systemd.service have more details (BTW, there's an index for all these things in man systemd.directives).


  1. You can redirect output using StandardOutput and StandardError parameters in the [Service] block, see man systemd.exec.

  2. There is an index of all service file directives in man systemd-directives, indicating which man page there are documented in, eg.:

    After=
        systemd.unit(5)
    
    Alias=
        systemd.unit(5)
    
8
  • 1
    Thank you, very useful answer and problem solved. Just a note, in my distro (Chakra Linux) default.target is not in /etc/systemd/system, but it's only in /usr/lib/systemd/system
    – eang
    Jan 18, 2013 at 22:37
  • Output from commands gets logged (where else could it go)?
    – vonbrand
    Jan 21, 2013 at 20:33
  • The /usr/lib/systemd/... files are fallback (default), you are supposed to drop yours in /etc/systemd/...
    – vonbrand
    Jan 21, 2013 at 20:34
  • 1
    @czerasz I notice on Fedora 27, if I systemctl set-default ... it leaves a symlink in /etc/systemd/system, but it does not change the one in /lib, i.e., they point at different targets, but stuff in the former should override the latter. If you've set it yourself that's what may happen. Anyway, I've edited in both locations.
    – goldilocks
    Feb 9, 2018 at 13:30
  • 1
    @HCSF [ I deleted my last comment reply, anyway: ] No, that's not a guarantee it will be the last. What I had there before about "WantedBy" and "After" default.target was redundant, since what man systemd.unit actually says is that After= means "the listed unit [in this case 'default.target' ] is fully started up before the configured unit [our unnamed example] is started up". "Started up" is pretty different than "finished". Anyway, I've substantially written the middle part above, hopefully that will be more helpful.
    – goldilocks
    Feb 15, 2020 at 15:37
2

For the systemd-tmpfiles service: it should ship with your distribution, but you can always get the service file from the upstream git-repository

You must log in to answer this question.

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