All Things IoT | Losant Blog

Intel Edison: Automatic Process Startup

Written by Michael Kuehl | Thu, Dec 10, 2015

So, you have an Intel Edison, and perhaps you got it all set up after following one of our previous blog posts, Getting Started with the Intel Edison.  But while the Edison is a general purpose computer (and plenty powerful enough to be used as one), usually you'll have a specific task in mind that you want to accomplish with it.  Once you have that task or program working the way you want, the next step is to "set it and forget it" - make it so no matter what happens or goes wrong, the Edison will continue to try and run that task or program.

The first obstacle to getting your Edison to the "set it and forget it" point is dealing with that most common of computer scenarios - the dreaded reboot.  Maybe the power went out, maybe the kernel panicked, but for whatever reason, your Edison is booting from scratch.  Today we're going to take a look at making sure your program gets automatically started as part of that boot.

Configuring Your Service

The default Linux distribution on the Edison is Yocto, which uses SystemD to manage the starting and stopping of services.  So the first step in getting a program to start automatically is to create a new service that SystemD will manage for us.  This isn't too difficult and mostly involves writing a service configuration file.  I have a process that controls my Phillips Hue light bulbs, so I'm going to start out with the full service configuration file for that program, and then walk through and explain each of the pieces.

The first section, Unit, describes what this service is and what it requires.  There are quite a few more possible options than the ones filled out above, but these three are almost certainly what you're going to need.  The first is pretty obvious - Description is just a description of what this service is.  The second, Wants, is a bit more complicated.  Essentially, you are listing here any other services that this service wants to also be running.  The most likely case is what I have listed here - the network-online.target.  What in the world is that, you might ask?  Network-online.target is a special "service" that represents when the Edison has network connectivity.  So this Wants line, combined with the After line, tells the system that not only do I want network connectivity, but I also want to not start my Hue light control service until after the Edison has a network connection.  If the After line wasn't here, the ordering of when the services started wouldn't be guaranteed - you would know that both services would be started, but it might start them both at the same time or might start the light control service before network connectivity was achieved. Note: neither the Wants nor After lines are required. Depending on your task or program, you may only need one or the other - and if your service doesn't depend on anything else, you don't have to have these lines at all.

Next up is the Service section.  This is where we get down to the real nuts and bolts of what we want to happen. Probably the most important line in the whole file is ExecStart - which says what to do to start this service.  In this case, my Hue light controller is just a Node.js program, and so I just run my program with Node (which is by default already installed on the Edison).  The next line, User, identifies what user to run the ExecStart line as - in this case, I'm running this node process under the nobody user.  Unless your program needs specific permissions or access, the nobody user is most likely what you want here, just to keep any security risks to a minimum.

After that, we have the very useful options of Restart and RestartSec.  These two describe what should happen if your program exits or crashes.  Here I have Restart set to on-failure, which is the recommended value for a long-running process that you want to ensure keeps running.  This means that if the program exits with anything other than a clean exit code, it will be automatically restarted.  The RestartSec line configures how long the system should wait before starting the process again - in this case, if my program crashes, the system will wait 10 seconds before starting it up again.

WorkingDirectory is pretty self-explanatory - you are just configuring the working directory for your program.  You may or may not need to set this, depending on the needs of your program.  The final line in the Service section, Environment, is setting an environment variable - here, the environment variable HUE_BRIDGE_IP is being set to 192.168.86.199.  Any environment variables set this way will be available just like normal inside of whatever process/program you are kicking off with ExecStart.  If you need to set multiple environment variables, you can just add more Environment lines to the service configuration file.

There are a lot more options that can be set in the Service section, but these basic ones will get you pretty far.  If you want to learn about the other possible options, read through some of the documentation here.

The final section in the service configuration file is Install.  Again, like the other sections, there are more options than just what I've configured - but in the common case, your Install section is going to look exactly like mine.  The one configuration set here is WantedBy.  WantedBy is a way to describe what actually wants this service to start - this is kind of the reverse of the Wants configuration line from the Unit section.  If you don't have a WantedBy configured, and no other service has your service listed in its Wants configuration, your service will never start on its own.  In our example, we want our service to start on boot (after all, that is the entire point of this blog post).  The multi-user.target is another one of those special "services" that represents when the system has booted normally (i.e., Linux running where multiple users can log in and work).  So if our service is "wanted" by the multi-user.target, it means that our service will be started on boot.

Enabling Your Service

Ok, so you have a service configuration file.  Now what?  Time to get the service enabled!  I'm going to assume that you have terminal access to your Edison (via ssh, serial, or whatever method you prefer) for the rest of this post, so if you don't, I recommend jumping back to read our Getting Started with the Intel Edison guide.  All of the commands described in the following should be run on a terminal on your Edison.

First, we have to get your new service file in the right spot.  All the service configuration files on the Edison are in the folder /lib/systemd/system/,  so you'll want to name your config file appropriately and place it in that folder.  I named mine hue-light-control.service (the file should end with the '.service' extension). Permissions on the file should be 644, and the user and group should both be root.

root@edison:~# mv ~/my_new_service_config /lib/systemd/system/hue-light-control.service
root@edison:~# chmod 644 /lib/systemd/system/hue-light-control.service
root@edison:~# chown root:root /lib/systemd/system/hue-light-control.service
root@edison:~# systemctl daemon-reload

The final line in the block above tells SystemD to reload all of its configuration so it will know about the new service. If you run the following command, you should get similar output: 

root@edison:~# systemctl --no-pager list-unit-files hue-light-control.service
UNIT FILE STATE
hue-light-control.service disabled

1 unit files listed.

So the system knows about the new service, but it is disabled.  Enabling it is done through another systemctl command:

root@edison:~# systemctl enable hue-light-control.service
ln -s '/lib/systemd/system/hue-light-control.service' '/etc/systemd/system/multi-user.target.wants/hue-light-control.service'
root@edison:~# systemctl --no-pager list-unit-files hue-light-control.service
UNIT FILE STATE
hue-light-control.service enabled

1 unit files listed.

Now it's enabled, which means that on boot the service will now start up automatically.   However, it's not started currently, which you can tell by asking for the status of the service:

root@edison:~# systemctl status hue-light-control.service
● hue-light-control.service - Hue Light Control Service
Loaded: loaded (/lib/systemd/system/hue-light-control.service; enabled)
Active: inactive (dead)

To start it right now, we have to explicitly start the service, using another systemctl command:

root@edison:~# systemctl start hue-light-control.service
root@edison:~# systemctl status hue-light-control.service
● hue-light-control.service - Hue Light Control Service
Loaded: loaded (/lib/systemd/system/hue-light-control.service; enabled)
Active: active (running) since Wed 2015-12-09 23:11:27 UTC; 17s ago
Main PID: 514 (node)
CGroup: /system.slice/hue-light-control.service
└─514 /usr/bin/node /usr/local/hue-light-control/worker.js

Dec 09 23:11:27 edison systemd[1]: Started Hue Light Control Service.
Dec 09 23:11:31 edison node[514]: Hue Worker Starting Up - Connecting To 192.168.86.199
Dec 09 23:11:44 edison node[514]: New Hue Value: 27033
root@edison:~# ps | grep "worker.js"
514 nobody 104m S /usr/bin/node /usr/local/hue-light-control/worker.js

As you can see, once it's started, there is a lot more output from the status command - including pid information and the last couple lines of logging about the service.  And if you run the ps command (with a bit of grep) as I did above, you can see the process running.

If you restart your Edison now and watch the system startup output, you should see your service start up on boot, similar to the output below:

....

[ OK ] Reached target Basic System.
Starting Edison PWR button handler...
[ OK ] Started Edison PWR button handler.
Starting Hue Light Control Service...
[ OK ] Started Hue Light Control Service.
Starting Daemon to load edison mcu app binary...
[ OK ] Started Daemon to load edison mcu app binary.

....

If you ever need to see logging or debug output for your service, you can use the journalctl command to inspect (or even tail!) the logs for your service:

root@edison:~# journalctl --no-pager -u hue-light-control.service
Dec 09 23:11:26 edison systemd[1]: Starting Hue Light Control Service...
Dec 09 23:11:27 edison systemd[1]: Started Hue Light Control Service.
Dec 09 23:11:44 edison node[514]: Hue Worker Starting Up - Connecting To 192.168.86.199
Dec 09 23:11:45 edison node[514]: New Hue Value: 32768
Dec 09 23:12:45 edison node[514]: New Hue Value: 27033
Dec 09 23:13:45 edison node[514]: New Hue Value: 29491
Dec 09 23:14:45 edison node[514]: New Hue Value: 27033

Now let's do a test of the automatic restarting of the service on failure (by SIGKILLing the process):

root@edison:~# kill -9 514
root@edison:~# systemctl status hue-light-control.service
● hue-light-control.service - Hue Light Control Service
Loaded: loaded (/lib/systemd/system/hue-light-control.service; enabled)
Active: activating (auto-restart) (Result: signal) since Wed 2015-12-09 23:20:02 UTC; 1s ago
Process: 514 ExecStart=/usr/bin/node /usr/local/hue-light-control/worker.js (code=killed, signal=KILL)
Main PID: 514 (code=killed, signal=KILL)
root@edison:~# sleep 10
root@edison:~# systemctl status hue-light-control.service
● hue-light-control.service - Hue Light Control Service
Loaded: loaded (/lib/systemd/system/hue-light-control.service; enabled)
Active: active (running) since Wed 2015-12-09 23:20:12 UTC; 1s ago
Main PID: 369 (node)
CGroup: /system.slice/hue-light-control.service
└─369 /usr/bin/node /usr/local/hue-light-control/worker.js
root@edison:~# journalctl -u hue-light-control.service -n 1000 --no-pager
Dec 09 23:20:00 edison node[514]: New Hue Value: 28672
Dec 09 23:20:02 edison systemd[1]: hue-light-control.service: main process exited, code=killed, status=9/KILLDec 09 23:20:02 edison systemd[1]: Unit hue-light-control.service entered failed state.
Dec 09 23:20:12 edison systemd[1]: hue-light-control.service holdoff time over, scheduling restart.
Dec 09 23:20:12 edison systemd[1]: Stopping Hue Light Control Service...
Dec 09 23:20:12 edison systemd[1]: Starting Hue Light Control Service...
Dec 09 23:20:12 edison systemd[1]: Started Hue Light Control Service.
Dec 09 23:20:17 edison node[369]: Hue Worker Starting Up - Connecting To 192.168.86.199
Dec 09 23:20:19 edison node[369]: New Hue Value: 28672

As you can see, after I killed the process, the system waited 10 seconds and then started the process up fresh! Now, If you want to actually stop the service or disable it from running on next boot, it's as simple as the stop and disable commands for systemctl:

root@edison:~# systemctl stop hue-light-control.service
root@edison:~# systemctl disable hue-light-control.service

And that's it!  That covers the basics of setting up, controlling, and inspecting services on the Edison - everything you need for that simple and common case of getting a process to start on boot.  If you have any questions or comments, feel free to leave them below, and I hope this made using your Edison just a bit easier!