16

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?

30

[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

For normal linux desktops this 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]
After=default.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, but nb. this does not determine when it will run in relation to others). Since we actually want this service to run later rather than sooner, we then specify an "After" clause. This does not actually need to be the same as the WantedBy target (it usually isn't) and can be completely omitted if you don't care when it happens; I'm just using it on the hunch that most other stuff will be run in relation to stuff that is somewhere chained to something that has specified Before=default.target (which we could also have used; a target's wants are appraised before the target is run).

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

[Service]
Type=forking
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. So /usr/local/bin/helloworld is a shell script with that one line, echo "hello world" > /dev/console.

Note the Type=forking, which is necessary for a shell script.

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.

  • 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 '13 at 22:37
  • Output from commands gets logged (where else could it go)? – vonbrand Jan 21 '13 at 20:33
  • The /usr/lib/systemd/... files are fallback (default), you are supposed to drop yours in /etc/systemd/... – vonbrand Jan 21 '13 at 20:34
  • These days default.target can be found in /lib/systemd/system/default.target – czerasz Feb 9 '18 at 7:33
  • 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 '18 at 13:30
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.