Creating a Service on a Raspberry Pi or Jetson Nano

Creating a service on a Raspberry Pi or a Jetson is easier than I thought. At the same time, there is still a lot of information to sort through. I’m still exploring the various settings that can be applied to a service. But I wanted to share the information that I thought would be immediately useful. While I was motivated to explore this based on something I was doing on a Jetson Nano, the code and instructions work identically without any need for modification on a Raspberry Pi.

I have a Jetson Mate. The Jetson Mate is an accessory for the Jetson Nano or Jetson Xavier NX Modules. Up to 4 modules can be placed within the Jetson mate to form a cluster. Really the Jetson Mate is just a convenient way to power multiple Jetsons and connect them to a wired network. It contains a 5-port switch so that a single Network cable can be used to connect all of the modules. Despite the Jetsons being in the same box, they don’t have an immediate way to know about each other. Reading the documentation from Seeed Studio, they suggest logging into your router and finding the IP addresses there.

That approach is fine when I’m using the Jetsons from my house; I have complete access to the Network here. But that’s not always possible. On some other networks I may not have access to the router settings. I made a program that would let the Jetson’s announce their presence over UDP Multicast. This could be useful on my Pis also; I run many of them as headless units. I needed for this program to start automatically after the device was powered on and to keep running. How do I do that? By making it a service.

There are several ways that one could schedule a task to run on Linux. I’m using systemd. Systemd was designed to unify service configurations across Linux distributions. The information shown here has applicability well beyond the Pi and Jetson.

The details of how my discovery program works is a discussion for another day. Let’s focus on what is necessary for making a service. For a sample service, let’s make a program that does nothing more than increment a variable and output the new value of the variable. The code that I show here is available on GitHub ( https://github.com/j2inet/sample-service ). But it is small enough to place here also. This is the program.

#include <iostream>
#include <thread>

using namespace std;

int main(int argc, char** argv) 
{
    int counter = 0;
    while(true)
    {
        cout << "This is cycle " << ++counter << endl;
        std::this_thread::sleep_for(std::chrono::seconds(10));
    }
}

This program counts, outputting a digit once ever ten seconds. To build the program, you will need to have cmake installed. To install it, use the following command at the terminal.

sudo apt-get install cmake -y

Once that is installed, from the project directory only a couple of commands are needed to compile the program.

cmake ./
make

The program is built, and a new executable named service-sample is now in the folder. If you run it, you will see the program counting. Press CTRL-C to terminate the program. Now we are going to make it into a service.

To make a service, you will need to copy the executable to a specific folder and also provide a file with the settings for the service. For the service settings, I’ve made a file named similarly to the executable. This isn’t a requirement. But it’s something that I’ve chosen to do for easier association. In a file named service-sample.service I’ve place the settings for the service. Many of these settings are technically optional; you only need to set many of them if your specific service is dependent on them. I’m showing more than is necessary for this service because I think some of these settings will be useful to you for other projects and wanted to provide an example.

[Unit]
Description=Counting service.
Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/service-sample
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

Here are what some of those settings mean. Note that I also describe some other settings that are not used, but available for you to consider. You can also see documentation for this file in the man pages.

[Unit] section

Documentation viewable with the following command

man systemd.unit

SettingMeaning
DescriptionA short text description of the service.
DocumentationURIs at which documentation for the service can be found
RequiresOther units that will be activated or deactivated in conjunction with this unit
WantsExpress weak dependencies. Will try to activate these dependencies first, but if those dependencies fail, this unit will be unaffected
ConflictsThis setting prevents this unit from running at the same time as a conflicting unit
After/BeforeUsed to express the order in which units are started.These settings contain a space delimited list of unit names.

[Install] Section

Documentation for the [Install] section is viewable at the following URL

SettingMeaning
RequiredBy / WantedByStarts the current service if any of the listed services are started. WantedBy is a weaker dependency than RequiredBy.
AlsoSpecifies services that are to be started or disabled along with this service

[Service] Section

Documentation for the [Service] section is viewable from the following URL.

man systemd.service

SettingMeaning
Type* simple – (default) starts the service immediately
* forking – the service is considered started once the process has forked and the parent has exited
* oneshot – similar to simple. Assumes service does its job and exits.
* notify – considers a service started when it sends a signal to systemd


ExecStartCommands with arguments to execute to start the service. Note that when Type=oneshot that multiple commands can be listed and executed sequentially.
ExecStopCommands to execute to stop the service
ExecReloadCommands to execute to trigger a configuration reload of the service
RestartWhen this option is enabled, the service will be restarted when the service process exits or is killed
RemainAfterExitWhen True, the service is considered active even after all of its processes have exited. Mostly used with Type=oneshot.

Deploying

Having the executable and this service file are not themselves enough. They must also be moved to an appropriate location and the service must be activated. I’ve placed the steps for doing this in a script. This script is intentionally a bit verbose to make it clear what the script is doing at any time. The first thing that this script does is terminate the service. While this might sound odd given that we haven’t installed the service yet, I do this to make the script rerunnable. If this is not the first time that the script has run, it is possible that the service process is running. To be safe, I terminate it.

Next, I copy files to their appropriate locations. For this simple service those files are one executable binary and the service settings. The executable is placed in /usr/local/bin. The service settings are copied to /etc/systemd/system/. On the service settings, the permissions on it are changed with chmod. This will ensure the owner has read/write permissions and the group has read permissions.

With the files for the service in place, we next ask systemd to reload the service definitions. I then probe the status for my service. While my service isn’t running, I should see it listed. I then enable the service (so that it will run on system startup) and then start the service (so that I don’t need to reboot to see it running now) and then probe the system status again.

echo "stopping service. Note that the service might not exists yet."
sudo systemctl stop service-sample

echo "--copying files to destination--"
sudo cp ./service-sample /usr/local/bin
sudo cp ./service-sample.service /etc/systemd/system/service-sample.service
echo "--setting permissiongs on file--"
sudo chmod 640 /etc/systemd/system/service-sample.service
echo "--reloading daemon and service definitions--"
sudo systemctl daemon-reload
echo "--probing service status--"
sudo systemctl status service-sample
echo "--enabling service--"
sudo systemctl enable service-sample
echo "--starting service service status--"
sudo systemctl start service-sample
echo "--probing service status--"
sudo systemctl status service-sample

After the service is installed and running, you can use the command for probing the status to see what it is up too. The last few lines that the service has outputted will display with the service information. Probe the service status at any time using this command.

sudo systemctl status service-sample

Sample output from the command follows.

pi@raspberrypi:~ $ sudo systemctl status service-sample
● service-sample.service - Counting service.
   Loaded: loaded (/etc/systemd/system/service-sample.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2022-03-09 15:57:12 HST; 12min ago
 Main PID: 389 (service-sample)
    Tasks: 1 (limit: 4915)
   CGroup: /system.slice/service-sample.service
           └─389 /usr/local/bin/service-sample

Mar 09 16:09:29 raspberrypi service-sample[389]: This is cycle 361
Mar 09 16:09:31 raspberrypi service-sample[389]: This is cycle 362
Mar 09 16:09:33 raspberrypi service-sample[389]: This is cycle 363
Mar 09 16:09:35 raspberrypi service-sample[389]: This is cycle 364
Mar 09 16:09:37 raspberrypi service-sample[389]: This is cycle 365
Mar 09 16:09:39 raspberrypi service-sample[389]: This is cycle 366
Mar 09 16:09:41 raspberrypi service-sample[389]: This is cycle 367
Mar 09 16:09:43 raspberrypi service-sample[389]: This is cycle 368
Mar 09 16:09:45 raspberrypi service-sample[389]: This is cycle 369
Mar 09 16:09:47 raspberrypi service-sample[389]: This is cycle 370
pi@raspberrypi:~ $
Screenshot of service output. Note the green dot indicates the service is running.

The real test for the service comes after reboot. Once you have the service installed and running on your Jetson or your Pi, reboot it. After it boots up, probe the status again. If you see output, then congratulations, your service is running! Now that a service can be easily created and registered, I’m going to refine the code that I used for discovery of the Pis and Jetsons for another post.

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Twitter: @j2inet
Instagram: @j2inet
Facebook: j2inet
YouTube: j2inet
Telegram: j2inet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.