Applying C - Running Programs With Systemd |
Written by Harry Fairhead | ||||||||||||||||||||||
Monday, 14 October 2019 | ||||||||||||||||||||||
Page 1 of 3 Getting a finished program to run is more complicated than you might think. In a modern Linux the solution is usually Systemd This extract is from my book on using C in an IoT context. Now available as a paperback or ebook from Amazon.Applying C For The IoT With Linux
Also see the companion book: Fundamental C <ASIN:1871962609> <ASIN:1871962617> One of the problems that you have to solve when you are getting near to moving from development to deployment is how to get your program to run. This can be as simple as using the command line, or automatically running the program when the machine starts up. There are lots of different ways of auto-running a program, but increasingly the solution is to use systemd. In this chapter we take a look at the basic idea of an executable and using systemd to control it. Earlier in the chapter but not in this extract:
Automatically Running a Program - systemdMost low-level programs need to run automatically when the system starts. There are quite a few different ways of doing this, but for a modern Linux-based system the best way of doing the job is to use systemd. It is slightly more complicated than some alternatives but it has many additional features. Systemd is often referred to as a replacement for the original Unix System V init and it will read and work with sysvinit configuration files. You will also find Linux systems that use alternatives to systemd - Upstart, runit, launchd and more. The only commonly encountered alternative running on smaller devices, such as within the mbed operating system, is BusyBox init, which is much simpler. Anything running full Linux is likely to support systemd. The basic object in systemd is a unit - something to be run when the system starts. There are a number of different types of unit but in most cases you will be interested in a service unit. A unit is defined by its configuration file, called a unit file. System supplied unit files are generally stored in /lib/systemd/system and any new unit files you create should be added to /etc/systemd/system which take priority over any unit files in the /lib directory with the same name. A unit file is a description of the program that you want systemd to run and how to do the job. It has the same name as the service you want to run and ends in .system. Typically there are three sections: [Unit] contains information not specifically related to the type of [Service] contains information about the specific type of the unit. [Install] contains information about the installation of the unit. Let's suppose you have a compiled C program, myservice, in a home directory /home/pi which simply prints a message every five seconds: #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char** argv) { while (1) { printf("Hello systemd world \n"); fflush(NULL); sleep(5); }; return (EXIT_SUCCESS); } To run this at startup you need to create it a unit file: [Unit] Description=My Hello Service [Service] Type=simple ExecStart=/home/pi/myservice Restart=on-failure RestartSec=10 KillMode=process [Install] WantedBy=multi-user.target This should be created in /etc/systemd/system and be called myService.service and you will need root privileges to do this. Let's look at what the different parts of the unit file do. The first section just contains a description: [Unit] Description=My Hello Service Description is used by systemd commands to refer to your service. The second section defines how your program should be run: [Service] Type=simple ExecStart=/home/pi/myservice Restart=on-failure RestartSec=10 KillMode=process Type sets the basic behavior of the program. The default is simple which is just a program that can be run from the command line. The ExecStart parameter is the command line instruction that starts your program running. In this case it is just the location of the executable, but in other cases it might start an interpreter and run a script. The Restart parameter tells systemd when to restart your program and RestartSec tells it how long to wait before doing so. Finally KillMode specifies how to stop your program; in this case by killing the process that is running your program. The final section tells systemd when to start your program running: [Install] WantedBy=multi-user.target this specifies which other units or targets depend on your program. In this case multi-user.target is used to specify that your program is to be started when the system reaches what used to be called runlevel2, i.e. the system is loaded enough to run multi-user consoles, but not a GUI. Other runlevel targets are:
It is also worth knowing that systemd is the first program to run after the kernel has been loaded and the file system has been set up. Notice that systemd also supports mount and device units which will perform what you might think were low level configuration after the kernel has loaded. WantedBy says that the target needs the program started, but nothing will happen if it doesn't start. You can also use Required by to state a hard dependence where the target will shut down if the unit doesn't start. There are many more options that can be used to specify dependencies and the order that units should be started in or restarted. The unit file listed above is a simple example to get you started on using systemd. |
||||||||||||||||||||||
Last Updated ( Monday, 14 October 2019 ) |