2

To get regular weather updates into my Redis database the scheme I am trying to use is as follows

I have a PHP script that fetches the weather from the relevant weather API. It goes something like this

<?php
 function getWeather()
 {
  if (weatherupdaterequired)
  {
   //weather API call
   //parse and store to database
  }
 }

 while (true)
 {
  getWeather();
  sleep(30);
 }
?>

which is stored in my /usr/local/bin folder. In the same folder I have a shell script, runweather which does just this:

#!/bin/sh
nohup php /usr/local/bin/echoweather.php  >/dev/null 2>&1 &

I normally tend to use #!/bin/bash but in this instance I found that when run at startup - as you will see below - only #!/bin/sh works. I assume that this has something to do with the bash shell not yet being available.

I then created a symlink to runweather

ln -s /usr/local/bin/runweather /etc/init.d/runweather

and then another symlink

ln -s /etc/init.d/runweather /etc/rc2.d/S99runweather

A few explanatory notes

  1. It is /usr/local/bin/echoweather.php that is doing all the real work. It runs at 30s intervals and sleeps when not working
  2. Just prior to terminating each run it places an ephemeral Redis key $redis-setEx("weatherreport",29,$echoCount)` which I can use to keep tabs on its health
  3. Placing the shell script that gets echoweather.php running at startup in /usr/local/bin, then symlinking it in /etc/init.d only to then symlink it again in /etc/rc2.d might look convoluted. I did this since I found that if I place the actual shell script in /etc/init.d and then symlink it to /etc/rc2.d it does not execute.

This scheme is working. I rebooted my server several times and checked on the health of echoweather.php by looking for the weatherreport key in Redis via redis-cli - always present and correct. However, I am a rank amateur when it comes to dealing with Ubuntu startup scripts. Perhaps there is a simpler way to do things? I'd be much obliged to anyone who might be able to comment.

DroidOS
  • 507

1 Answers1

4

If you're not running a EOL version of Ubuntu, your init system is already systemd based, so using it is the right way to go. Some people might tell you to use Type=idle for your service unit, but please: (from the systemd man page):

Note that using any type other than simple possibly delays the boot process, as the service manager needs to wait for service initialization to complete. It is hence recommended not to needlessly use any types other than simple. (Also note it is generally not recommended to use idle or oneshot for long-running services.)

Taking that into consideration, we should manage our service ordering by setting targets and dependencies. You have at least three important requirements for your script to work:

  1. Your network stack needs to be functioning;
  2. Your script should run late in the boot process, and;
  3. Your script should run after Redis is fully started.

Having identified those requirements, let's take a look at the systemd targets so we know what we want, require and should depend or wait on:

  1. Units that strictly requires a configured network connection should pull in network-online.target, so our unit Wants this and should only run After this target;
  2. multi-user.target sets up a multi-user system and finishes up once everything required for that is done, so the unit probably Wants and is WantedBy it. As we really want our unit to run late into the boot process, running it After this target is what we want too;
  3. Redis is part of the multi-user system we are configuring, and our unit can only produce its desired effects if Redis up and running so it Requires Redis to be running and we should only load After it.

With that in mind, we can write a echoweather.service file into the /etc/systemd/system/ that takes all of this into account:

[Unit]
Description=Gets regular weather updates into my Redis database
Wants=network-online.target multi-user.target
Requires=redis-server.service
After=network-online.target multi-user.target redis-server.service

[Service]
PIDFile=/var/run/echoweather.pid
ExecStart=/usr/bin/php /usr/local/bin/echoweather.php  >/dev/null 2>&1 &
Type=forking
KillMode=process

[Install]
WantedBy=multi-user.target

Once it's done, reload your daemon files, enable your service unit and run your process:

$ sudo systemctl daemon-reload
$ sudo systemctl enable echoweather.service
$ sudo systemctl start echoweather.service
Alexandre Teles
  • 1,802
  • 2
  • 12
  • 16