[#]: collector: (lujun9972) [#]: translator: ( ) [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( ) [#]: subject: (Manage startup using systemd) [#]: via: (https://opensource.com/article/20/5/manage-startup-systemd) [#]: author: (David Both https://opensource.com/users/dboth) Manage startup using systemd ====== Learn how systemd determines the order services start, even though it is essentially a parallel system. ![Penguin with green background][1] While setting up a Linux system recently, I wanted to know how to ensure that dependencies for services and other units were up and running before those dependent services and units start. Specifically, I needed more knowledge of how systemd manages the startup sequence, especially in determining the order services are started in what is essentially a parallel system. You may know that SystemV (systemd's predecessor, as I explained in the [first article][2] in this series) orders the startup sequence by naming the startup scripts with an SXX prefix, where XX is a number from 00 to 99. SystemV then uses the sort order by name and runs each start script in sequence for the desired runlevel. But systemd uses unit files, which can be created or modified by a sysadmin, to define subroutines for not only initialization but also for regular operation. In the [third article][3] in this series, I explained how to create a mount unit file. In this fifth article, I demonstrate how to create a different type of unit file—a service unit file that runs a program at startup. You can also change certain configuration settings in the unit file and use the systemd journal to view the location of your changes in the startup sequence. ### Preparation Make sure you have removed `rhgb` and `quiet` from the `GRUB_CMDLINE_LINUX=` line in the `/etc/default/grub` file, as I showed in the [second article][4] in this series. This enables you to observe the Linux startup message stream, which you'll need for some of the experiments in this article. ### The program In this tutorial, you will create a simple program that enables you to observe a message during startup on the console and later in the systemd journal. Create the shell program `/usr/local/bin/hello.sh` and add the following content. You want to ensure that the result is visible during startup and that you can easily find it when looking through the systemd journal. You will use a version of the "Hello world" program with some bars around it, so it stands out. Make sure the file is executable and has user and group ownership by root with [700 permissions][5] for security: ``` #!/usr/bin/bash # Simple program to use for testing startup configurations # with systemd. # By David Both # Licensed under GPL V2 # echo "###############################" echo "######### Hello World! ########" echo "###############################" ``` Run this program from the command line to verify that it works correctly: ``` [root@testvm1 ~]# hello.sh ############################### ######### Hello World! ######## ############################### [root@testvm1 ~]# ``` This program could be created in any scripting or compiled language. The `hello.sh` program could also be located in other places based on the [Linux filesystem hierarchical structure][6] (FHS). I place it in the `/usr/local/bin` directory so that it can be easily run from the command line without having to prepend a path when I type the command. I find that many of the shell programs I create need to be run from the command line and by other tools such as systemd. ### The service unit file Create the service unit file `/etc/systemd/system/hello.service` with the following content. This file does not need to be executable, but for security, it does need user and group ownership by root and [644][7] or [640][8] permissions: ``` # Simple service unit file to use for testing # startup configurations with systemd. # By David Both # Licensed under GPL V2 # [Unit] Description=My hello shell script [Service] Type=oneshot ExecStart=/usr/local/bin/hello.sh [Install] WantedBy=multi-user.target ``` Verify that the service unit file performs as expected by viewing the service status. Any syntactical errors will show up here: ``` [root@testvm1 ~]# systemctl status hello.service ● hello.service - My hello shell script      Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)      Active: inactive (dead) [root@testvm1 ~]# ``` You can run this "oneshot" service type multiple times without problems. The oneshot type is intended for services where the program launched by the service unit file is the main process and must complete before systemd starts any dependent process. There are seven service types, and you can find an explanation of each (along with the other parts of a service unit file) in the [systemd.service(5)][9] man page. (You can also find more information in the [resources][10] at the end of this article.) As curious as I am, I wanted to see what an error might look like. So, I deleted the "o" from the `Type=oneshot` line, so it looked like `Type=neshot`, and ran the command again: ``` [root@testvm1 ~]# systemctl status hello.service ● hello.service - My hello shell script      Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)      Active: inactive (dead) May 06 08:50:09 testvm1.both.org systemd[1]: /etc/systemd/system/hello.service:12: Failed to parse service type, ignoring: neshot [root@testvm1 ~]# ``` These results told me precisely where the error was and made it very easy to resolve the problem. Just be aware that even after you restore the `hello.service` file to its original form, the error will persist. Although a reboot will clear the error, you should not have to do that, so I went looking for a method to clear out persistent errors like this. I have encountered service errors that require the command `systemctl daemon-reload` to reset an error condition, but that did not work in this case. The error messages that can be fixed with this command always seem to have a statement to that effect, so you know to run it. It is, however, recommended that you run `systemctl daemon-reload` after changing a unit file or creating a new one. This notifies systemd that the changes have been made, and it can prevent certain types of issues with managing altered services or units. Go ahead and run this command. After correcting the misspelling in the service unit file, a simple `systemctl restart hello.service` cleared the error. Experiment a bit by introducing some other errors into the `hello.service` file to see what kinds of results you get. ### Start the service Now you are ready to start the new service and check the status to see the result. Although you probably did a restart in the previous section, you can start or restart a oneshot service as many times as you want since it runs once and then exits. Go ahead and start the service (as shown below), and then check the status. Depending upon how much you experimented with errors, your results may differ from mine: ``` [root@testvm1 ~]# systemctl start hello.service [root@testvm1 ~]# systemctl status hello.service ● hello.service - My hello shell script      Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)      Active: inactive (dead) May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ######## May 10 10:37:49 testvm1.both.org hello.sh[842]: ############################### May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded. May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script. May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script... May 10 10:54:45 testvm1.both.org hello.sh[1380]: ############################### May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ######## May 10 10:54:45 testvm1.both.org hello.sh[1380]: ############################### May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded. May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script. [root@testvm1 ~]# ``` Notice in the status command's output that the systemd messages indicate that the `hello.sh` script started and the service completed. You can also see the output from the script. This display is generated from the journal entries of the most recent invocations of the service. Try starting the service several times, and then run the status command again to see what I mean. You should also look at the journal contents directly; there are multiple ways to do this. One way is to specify the record type identifier, in this case, the name of the shell script. This shows the journal entries for previous reboots as well as the current session. As you can see, I have been researching and testing for this article for some time now: ``` [root@testvm1 ~]# journalctl -t hello.sh <snip> \-- Reboot -- May 08 15:55:47 testvm1.both.org hello.sh[840]: ############################### May 08 15:55:47 testvm1.both.org hello.sh[840]: ######### Hello World! ######## May 08 15:55:47 testvm1.both.org hello.sh[840]: ############################### \-- Reboot -- May 08 16:01:51 testvm1.both.org hello.sh[840]: ############################### May 08 16:01:51 testvm1.both.org hello.sh[840]: ######### Hello World! ######## May 08 16:01:51 testvm1.both.org hello.sh[840]: ############################### \-- Reboot -- May 10 10:37:49 testvm1.both.org hello.sh[842]: ############################### May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ######## May 10 10:37:49 testvm1.both.org hello.sh[842]: ############################### May 10 10:54:45 testvm1.both.org hello.sh[1380]: ############################### May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ######## May 10 10:54:45 testvm1.both.org hello.sh[1380]: ############################### [root@testvm1 ~]# ``` To locate the systemd records for the `hello.service` unit, you can search on systemd. You can use **G+Enter** to page to the end of the journal entries and then scroll back to locate the ones you are interested in. Use the `-b` option to show only the entries for the most recent startup: ``` [root@testvm1 ~]# journalctl -b -t systemd <snip> May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image.... May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image.. May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded. May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script. May 10 10:37:50 testvm1.both.org systemd[1]: Starting D-Bus System Message Bus... May 10 10:37:50 testvm1.both.org systemd[1]: Started D-Bus System Message Bus. ``` I copied a few other journal entries to give you an idea of what you might find. This command spews all of the journal lines pertaining to systemd—109,183 lines when I wrote this. That is a lot of data to sort through. You can use the pager's search facility, which is usually `less`, or you can use the built-in `grep` feature. The `-g` (or `--grep=`) option uses Perl-compatible regular expressions: ``` [root@testvm1 ~]# journalctl -b -t systemd -g "hello" [root@testvm1 ~]# journalctl -b -t systemd -g "hello" \-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:01:01 EDT. -- May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script... May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded. May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script. May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script... May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded. May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script. [root@testvm1 ~]# ``` You could use the standard GNU `grep` command, but that would not show the log metadata in the first line. If you do not want to see just the journal entries pertaining to your `hello` service, you can narrow things down a bit by specifying a time range. For example, I will start with the beginning time of `10:54:00` on my test VM, which was the start of the minute the entries above are from. ****Note that the `--since=` option must be enclosed in quotes and that this option can also be expressed as `-S "