[Ruby] Ruby script service (systemd + rvm + execute with user privileges)

3 minute read

Introduction

The script for GPS log made by Using soft UART on Raspberry Pi 3 is unexpectedly unstable, so it restarts even if the program ends with an exception Try using systemd like this.

Premise

  • rvm environment
  • Convert the ruby script into a service with user privileges
  • use systemd

Goal

  • Restart the GPS log script if it crashes due to an unexpected error

See the official #rvm docs

The official page below was created for Gugu, but I didn’t understand it because it was too brief to explain, so I decided to try it.

https://rvm.io/deployment/init-d

You should be able to use this procedure with cron, etc. (although I haven’t tried :sweat_drops:)

Procedure

The procedure is basically as follows.

  • make rvm wrapper
  • Create a systemd Unit definition file -Execute script via wrapper in ExecStart

make wrapper for rvm

First, check the rmv path.

[email protected]:~ $ echo $rvm_path
/home/pi/.rvm

New gem set in wrappers under that path? Create it, so let’s look ahead just in case.

[email protected]:~ $ll ~/.rvm/wrappers/
total 0
lrwxrwxrwx 1 pi pi 33 Jul 18 16:22 default -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 38 Jul 18 16:22 ruby-2.7.1 -> /home/pi/.rvm/gems/ruby-2.7.1/wrappers
lrwxrwxrwx 1 pi pi 45 Jul 18 16:22 [email protected] -> /home/pi/.rvm/gems/[email protected]/wrappers

Create a Gem set with the following command. This time I created it with the name gps. I matched it with the official description, but I don’t know why you need to specify two gps and the app name…

[email protected]:~ $ rvm alias create gps [email protected]
Gemset'gps' does not exist,'rvm ruby-2.7.1 do rvm gemset create gps' first, or append'--create'.
Creating alias gps for ruby-2.7.1.....

As expected, I got an error, but I was able to create it properly as follows, so let’s be okay: sweat_smile:

[email protected]:~ $ll ~/.rvm/wrappers/
total 0
lrwxrwxrwx 1 pi pi 33 Jul 18 16:22 default -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 33 Jul 19 20:01 gps -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 38 Jul 18 16:22 ruby-2.7.1 -> /home/pi/.rvm/gems/ruby-2.7.1/wrappers
lrwxrwxrwx 1 pi pi 45 Jul 18 16:22 [email protected] -> /home/pi/.rvm/gems/[email protected]/wrappers

create a unit definition file for systemd

Then just create as usual. The contents of this time are as follows. Specify the directory where the script is located in WorkingDirectory, and execute the bundle command via wrapper in ExecStart. Restart=always is important: relaxed:

[email protected]:~ $ cat /etc/systemd/system/gps.service
[Unit]
Description=Gps Service
After=network.target

[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/project/gps
ExecStart=/home/pi/.rvm/wrappers/gps/bundle exe ruby main.rb
Restart=always

[Install]
WantedBy=multi-user.target

Modify the script a little

I tried to put a process that falls due to a logger and an exception in the work.

require'serialport'
require'nmea_plus'
require'logger'

logger = Logger.new('gps.log')

logger.info "Launched"

puts `sudo rmmod soft_uart.ko`
sleep 0.1
puts `cd ~/project/soft_uart && sudo insmod soft_uart.ko`

sp = SerialPort.new('/dev/ttySOFT0', 9600, 8, 1, 0) # see: https://rubydoc.info/gems/serialport/SerialPort#set_modem_params-instance_method

trap'SIGINT' do
  sp.close if sp
  exit
end

count = 0

source_decorder = NMEAPlus::SourceDecoder.new(sp)
source_decorder.each_complete_message do |message|
  # count up
  count +=1

  # see: https://github.com/ianfixes/nmea_plus/blob/master/lib/nmea_plus/message/nmea/rmc.rb
  if'GPRMC' == message.data_type
    logger.info message.utc_time
    logger.info message.active? # false: Data is invalid
    logger.info message.latitude
    logger.info message.longitude
    logger.info message.faa_mode # A: Single positioning (accuracy around 3m), D: Relative positioning (accuracy around 0.4m)
  end

  if count >= 10
    sp.close # Try closing it for now
    logger.error "I will drop it."
    raise'brute force'
  end
end

Check

First, reload systemd itself and then start the service.

sudo systemctl daemon-reload
sudo systemctl start gps.service

The log is… nice! This makes the unstable script a little stronger.

[email protected]:~ $ tail -f ./project/gps/gps.log
I, [2020-07-19T20:59:05.521956 #6437] INFO --: Started
I, [2020-07-19T20:59:06.651867 #6437] INFO --: 2020-07-19 11:59:06 +0000
I, [2020-07-19T20:59:06.652419 #6437] INFO --: true
I, [2020-07-19T20:59:06.652937 #6437] INFO --: 33.725631666666665
I, [2020-07-19T20:59:06.653450 #6437] INFO --: 131.64383833333332
I, [2020-07-19T20:59:06.654003 #6437] INFO --: A
I, [2020-07-19T20:59:07.652485 #6437] INFO --: 2020-07-19 11:59:07 +0000
I, [2020-07-19T20:59:07.653032 #6437] INFO --: true
I, [2020-07-19T20:59:07.653515 #6437] INFO --: 33.725631666666665
I, [2020-07-19T20:59:07.654019 #6437] INFO --: 131.64384
I, [2020-07-19T20:59:07.654624 #6437] INFO --: A
E, [2020-07-19T20:59:07.694013 #6437] ERROR --: Drop it
I, [2020-07-19T20:59:12.530348 #6460] INFO --: Started
I, [2020-07-19T20:59:13.660674 #6460] INFO --: 2020-07-19 11:59:13 +0000
I, [2020-07-19T20:59:13.661174 #6460] INFO --: true
I, [2020-07-19T20:59:13.661502 #6460] INFO --: 33.72563
I, [2020-07-19T20:59:13.661792 #6460] INFO --: 131.64383666666666
I, [2020-07-19T20:59:13.662027 #6460] INFO --: A
I, [2020-07-19T20:59:14.660078 #6460] INFO --: 2020-07-19 11:59:14 +0000
I, [2020-07-19T20:59:14.660613 #6460] INFO --: true
I, [2020-07-19T20:59:14.660962 #6460] INFO --: 33.72562833333333
I, [2020-07-19T20:59:14.661288 #6460] INFO --: 131.64383666666666
I, [2020-07-19T20:59:14.661528 #6460] INFO --: A
E, [2020-07-19T20:59:14.662166 #6460] ERROR --: Drop it

Impression-Which is easier for rbenv or rvm to make the script a service?

  • I don’t know if the official documentation is too rough…

Tags:

Updated: