Introduction

In an earlier article, we discussed the systemd system startup manager in detail and also ventured into the reasons why it has replaced sysvinit as the default system startup and service manager for many popular Linux distributions including RedHat, Centos, and Ubuntu. Within Centos 7/RHEL 7 we are provided with targets or target units instead of run levels and in a similar manner service units now represent service scripts in systemd. In this article, we will demonstrate how we can create service unit files which are analogous to the init scripts in sysvinit which we already covered in a previous article.

 

An overview of systemctl
One of the interesting features of systemd is that run level management and service management are under the control of a unified tool named systemctl. Therefore, systemd does not use the service and chkconfig commands or the init or telinit command to change run levels.

To enable or start a service, for example, sshd on boot, we would use the following command.

systemctl enable sshd

To disable the sshd service on boot, we would use the following command.

systemctl start sshd

To view the status of the sshd service, we would use the following command.

systemctl status sshd

The systemctl status command is especially more useful and verbose as compared to it’s predecessor, the service status command because it not only provides the PID of the process running the service but also displays if the service is enabled on boot or not and also provides a snippet from the journal log for the service.

 

Creating a service unit file

Package managers install service unit files (service startup scripts) for applications within the /lib/systemd/system directory. The suffix to the file is .service. When using systemctl it’s assumed that you are referring to a service.
run levels are .target files. Given below is a simple service unit file that I’ve created for a service named my.service

[root@linuxnix system]# cat my.service
[Unit]

Description=MyService
 After=sshd.service

[Service]
 ExecStart=/usr/bin/printf "Hello There"

[Install]
 WantedBy=multi-user.target

 

To start the service, use the following command:

[root@linuxnix system]# systemctl start my.service

 

Explanation

Unit Stanza

The unit file begins with the unit stanza. Here we provide a brief description of the service followed by a sort of service dependency i.e. the service whose unit file we are writing should start after the service specified in the ‘After’ directive in the unit stanza.

Service Stanza
Next, we have the Service stanza. Here we type the command which we need to execute the service.

Install stanza
Finally, under the install section, we specify the run level or boot target in which the script should run.
In this example, we want the service to be available in multi-user.target i.e. run level 3.

 

Notice that when we started the service we didn’t see any output on the screen although we had specified a print statement in the service unit file. The reason for this is that we didn’t specify a tty where the output should be directed. So in this case, the output of the print command was directed to the journal. We can query the journal log for this service using the following command:

[root@linuxnix system]# journalctl -u my.service
-- Logs begin at Sat 2018-02-03 13:47:50 IST, end at Sat 2018-02-03 14:24:26 IST. --
Feb 03 14:24:26 linuxnix systemd[1]: Started MyService.
Feb 03 14:24:26 linuxnix systemd[1]: Starting MyService...
Feb 03 14:24:26 linuxnix printf[3177]: Hello There
[root@linuxnix system]#

 

Triggering a service based on an event
The service unit we created just now was somewhat similar to how we would configure an init script i.e. we created the unit file and started the service. To enable the service on boot, we would type systemctl enable my.service But what if we wanted to trigger the service based on an event like the presence of a file. Using stub files is common in system administration related scripts wherein we perform or not perform a task based on the absence or presence of a file. We’ll now see how would configure something similar with service units.

Given below is a service unit file that I created named sahil.service

[root@linuxnix system]# cat sahil.service
[Unit]
Description="Print Reminder"
[Service]
ExecStart=/usr/bin/printf "Stub file created"
[root@linuxnix system]#

Notice that the file does not create an install stanza. The service has not been started and if we take a look at the journal log we will see that it doesn’t have much content.

[root@linuxnix system]# journalctl -u sahil.service
-- Logs begin at Sat 2018-02-03 13:47:50 IST, end at Sat 2018-02-03 14:32:59 IST. --
Feb 03 14:32:51 linuxnix systemd[1]: [/usr/lib/systemd/system/sahil.service:2] Unknown lvalue 'DEscription' in section 'Unit'
Feb 03 14:32:51 linuxnix systemd[1]: [/usr/lib/systemd/system/sahil.service:2] Unknown lvalue 'DEscription' in section 'Unit'

Now let’s take a look at the path file that I’ve created for this service.

[root@linuxnix system]# cat sahil.path
[Unit]
Description="Check for stub file"
After=sshd.service
[Path]
PathExists=/tmp/sahil
[Install]
WantedBy=multi-user.target
[root@linuxnix system]#

The path file is actually what we will enable and start. This path file will search for the file /tmp/sahil and when it finds this file it will start the service sahil. So, let’s enable and start the service defined in the path file.

[root@linuxnix system]# systemctl enable sahil.path
[root@linuxnix system]# systemctl start sahil.path

Now, we will create the file /tmp/sahil.

[root@linuxnix system]# touch /tmp/sahil

The service sahil.service should have been started by sahil.path after the creation of file /tmp/sahil. Let’s verify it’s journal log:

[root@linuxnix system]# journalctl -u sahil.service
-- Logs begin at Sat 2018-02-03 13:47:50 IST, end at Sat 2018-02-03 14:33:33 IST. --
Feb 03 14:32:51 linuxnix systemd[1]: [/usr/lib/systemd/system/sahil.service:2] Unknown lvalue 'DEscription' in section 'Unit'
Feb 03 14:32:51 linuxnix systemd[1]: [/usr/lib/systemd/system/sahil.service:2] Unknown lvalue 'DEscription' in section 'Unit'
Feb 03 14:33:33 linuxnix systemd[1]: Started sahil.service.
Feb 03 14:33:33 linuxnix systemd[1]: Starting sahil.service...
Feb 03 14:33:33 linuxnix printf[3351]: Stub file created
[root@linuxnix system]#

As you can see the service sahil.service started and appended the output of the print command to the journal log.

Let’s take a look at the status of the service as well as the corresponding path.

[root@linuxnix system]# systemctl status sahil
● sahil.service
Loaded: loaded (/usr/lib/systemd/system/sahil.service; static; vendor preset: disabled)
Active: inactive (dead) since Sat 2018-02-03 14:33:33 IST; 1h 0min ago
Process: 3351 ExecStart=/usr/bin/printf Stub file created (code=exited, status=0/SUCCESS)
Main PID: 3351 (code=exited, status=0/SUCCESS)
Feb 03 14:33:33 linuxnix systemd[1]: Started sahil.service.
 Feb 03 14:33:33 linuxnix systemd[1]: Starting sahil.service...
 Feb 03 14:33:33 linuxnix printf[3351]: Stub file created
 Warning: sahil.service changed on disk. Run 'systemctl daemon-reload' to reload units.

 

[root@linuxnix system]# systemctl status sahil.path
● sahil.path - "Check for stub file"
 Loaded: loaded (/usr/lib/systemd/system/sahil.path; enabled; vendor preset: disabled)
 Active: active (waiting) since Sat 2018-02-03 14:32:59 IST; 1h 0min ago

Feb 03 14:32:59 linuxnix systemd[1]: Started "Check for stub file".
Feb 03 14:32:59 linuxnix systemd[1]: Starting "Check for stub file".
[root@linuxnix system]# rm /tmp/sahil
rm: remove regular empty file ‘/tmp/sahil’? y
[root@linuxnix system]# systemctl status sahil.path
● sahil.path - "Check for stub file"
 Loaded: loaded (/usr/lib/systemd/system/sahil.path; enabled; vendor preset: disabled)
 Active: active (waiting) since Sat 2018-02-03 14:32:59 IST; 1h 1min ago

 

From the above output, you may ascertain that the service has exited successfully because it completed its task of executing a print statement and hence the process for the service was terminated once the print statement was executed. The sahil.path file, however, is in waiting state. This is because it is monitoring the presence of the stub file /tmp/sahil. So, if we remove and re-create the file /tmp/sahil, then this event will be recognized by the sahil.path file and the service sahil will be started and executed again.

 

Conclusion

In this article, we discussed some of the different stanzas that form a service unit file and we also demonstrated how you could configure a simple service unit. We then expanded on our first example and showed how to create an event-driven service unit file. We hope that you’ve found this article to be useful and we encourage you to try more examples of creating service units as it’s an extremely useful procedure to have knowledge of.

The following two tabs change content below.

Sahil Suri

He started his career in IT in 2011 as a system administrator. He has since worked with HP-UX, Solaris and Linux operating systems along with exposure to high availability and virtualization solutions. He has a keen interest in shell, Python and Perl scripting and is learning the ropes on AWS cloud, DevOps tools, and methodologies. He enjoys sharing the knowledge he's gained over the years with the rest of the community.