Systemd: Difference between revisions

From VDR Wiki
Jump to navigation Jump to search
m (Move shutdown up in the hiearchy)
(Add a policy-kit rule for preventing shutdown)
Line 3: Line 3:


Since 2015, the majority of Linux distributions have adopted systemd, having replaced other init systems such as SysV init.
Since 2015, the majority of Linux distributions have adopted systemd, having replaced other init systems such as SysV init.

==Preventing shutdown and reboot while VDR is running==
By default, the power button on a [[remote control]] that is managed by a kernel [[LIRC]] driver will be mapped to <code>systemd-logind</code>. Unless you have overridden the default <code>HandlePowerKey=poweroff</code> in <code>/etc/systemd/logind.conf</code>, the system would be shut down immediately when you hit a power button on any input device (remote control, keyboard, or power button on the computer case).

Often, VDR is the main application on the system. You would not want the system to be accidentally shut down or rebooted while someone is watching TV, or a recording is in progress. System shutdown would be initiated by a script that would be invoked by VDR itself; see the <code>-s</code> parameter of <code>ExecStart</code> below.

To disable or enable the normal handling of shutdown and reboot, you can use the following script, say, <code>/etc/systemd/system/vdr-keep-alive.sh</code>. Executing it will require super user privileges.
<pre>
#!/bin/sh

TARGETS="
/lib/systemd/system/poweroff.target.d
/lib/systemd/system/reboot.target.d
/lib/systemd/system/halt.target.d
"
CONF=vdr-keep-alive.conf

case "$1" in
start)
for t in $TARGETS
do
if [ ! -f "$t/$CONF" ]
then
if [ ! -d "$t/" ]
then
mkdir "$t"
fi
echo "[Unit]\nRefuseManualStart=yes" > "$t/$CONF"
fi
done
;;
stop)
for t in $TARGETS
do
rm -f "$t/$CONF"
done
;;
esac

exec systemctl daemon-reload
</pre>
You can execute this script as follows:
<pre>
sudo /etc/systemd/system/vdr-keep-alive.sh start
sudo /etc/systemd/system/vdr-keep-alive.sh stop
</pre>
You might want to make the <code>TARGETS</code> above to include <code>suspend</code>, <code>hibernate</code>, or others mentioned in <code>man 7 systemd.special</code>.


==Making Systemd start up VDR==
==Making Systemd start up VDR==
Line 65: Line 18:
[Service]
[Service]
User=pi
User=pi
ExecStartPre=+/etc/systemd/system/vdr-keep-alive.sh start
#ExecStartPre=+/etc/systemd/system/vdr-keep-alive.sh start
ExecStart=/usr/local/bin/vdr --no-kbd --lirc=/dev/lirc0 -Prpihddevice -v /video/video -s /var/lib/vdr/vdr-shutdown.sh
ExecStart=/usr/local/bin/vdr --no-kbd --lirc=/dev/lirc0 -Prpihddevice -v /video/video -s /var/lib/vdr/vdr-shutdown.sh
TimeoutStartSec=infinity
TimeoutStartSec=infinity
Line 81: Line 34:


The shutdown script (named <code>/var/lib/vdr/vdr-shutdown.sh</code> in the above example) could do at least one of the following:
The shutdown script (named <code>/var/lib/vdr/vdr-shutdown.sh</code> in the above example) could do at least one of the following:
*Invoke <code>sudo /etc/systemd/system/vdr-keep-alive.sh stop</code> to allow normal reboot and shutdown.
*Adjust [[rtcwake]] (or [[Nvram_wakeup]]) to have the system start up automatically on the next scheduled recording.
*Adjust [[rtcwake]] (or [[Nvram_wakeup]]) to have the system start up automatically on the next scheduled recording.
*Invoke <code>sudo systemctl stop vdr</code> or <code>sudo systemctl halt</code> to shut down VDR or the entire system. This will also terminate the shell that runs the script!
*Invoke <code>sudo systemctl stop vdr</code> or <code>sudo systemctl halt</code> to shut down VDR or the entire system. This will also terminate the shell that runs the script!
Line 157: Line 109:
To notify systemd of the update:
To notify systemd of the update:
<pre>sudo systemctl reenable vdr</pre>
<pre>sudo systemctl reenable vdr</pre>

==Preventing shutdown and reboot while VDR is running==
By default, the power button on a [[remote control]] that is managed by a kernel [[LIRC]] driver will be mapped to <code>systemd-logind</code>. Unless you have overridden the default <code>HandlePowerKey=poweroff</code> in <code>/etc/systemd/logind.conf</code>, the system would be shut down immediately when you hit a power button on any input device (remote control, keyboard, or power button on the computer case).

Often, VDR is the main application on the system. You would not want the system to be accidentally shut down or rebooted while someone is watching TV, or a recording is in progress. System shutdown would be initiated by a script that would be invoked by VDR itself; see the <code>-s</code> parameter of <code>ExecStart</code> above.

===Policykit script===
Policykit after version 105 should support Javascript based rules. Older versions only supported a <code>.pkla</code> format. The place for custom user rules is the directory <code>/etc/polkit-1/rules.d/</code>.

Create the file <code>/etc/polkit-1/rules.d/50-vdr-prevent-shutdown.rules</code> with the following contents:
<pre>
const power_actions = [
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions"
];

polkit.addRule (function (action, subject) {
if (power_actions.includes(action.id)) {
try {
polkit.spawn(["/usr/bin/test", "!", "-d", "/video/video"]);
return polkit.Result.YES;
}
catch (error) {
return polkit.Result.NO;
}
}
});
</pre>
This will prevent the power actions as long as the directory <code>/video/video</code> exists, which could mean that a detachable video storage is plugged in. You may want to replace the <code>polkit.spawn</code> call with something else.

Commands like <code>systemctl poweroff -i</code> will work, because the script does not cover actions like <code>org.freedesktop.login1.power-off-ignore-inhibit</code>.

===Old way: dynamically reconfiguring systemd===

You might use the following script, say, <code>/etc/systemd/system/vdr-keep-alive.sh</code>. You could edit the <code>TARGETS</code> below to include <code>suspend</code>, <code>hibernate</code>, or others mentioned in <code>man 7 systemd.special</code>. Executing the script will require super user privileges.
<pre>
#!/bin/sh

TARGETS="
/lib/systemd/system/poweroff.target.d
/lib/systemd/system/reboot.target.d
/lib/systemd/system/halt.target.d
"
CONF=vdr-keep-alive.conf

case "$1" in
start)
for t in $TARGETS
do
if [ ! -f "$t/$CONF" ]
then
if [ ! -d "$t/" ]
then
mkdir "$t"
fi
echo "[Unit]\nRefuseManualStart=yes" > "$t/$CONF"
fi
done
;;
stop)
for t in $TARGETS
do
rm -f "$t/$CONF"
done
;;
esac

exec systemctl daemon-reload
</pre>
You can execute this script as follows:
<pre>
sudo /etc/systemd/system/vdr-keep-alive.sh start
sudo /etc/systemd/system/vdr-keep-alive.sh stop
</pre>


==Shutting down VDR==
==Shutting down VDR==
Line 171: Line 203:
then
then
sudo rtcwake -m no -s "$2" || :
sudo rtcwake -m no -s "$2" || :
sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
#sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
exec sudo udisksctl power-off -b "$STORAGE"
exec sudo udisksctl power-off -b "$STORAGE"
elif [ "$2" = 0 ]
elif [ "$2" = 0 ]
then
then
sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
sudo rtcwake -m disable || :
sudo rtcwake -m disable || :
#sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
exec sudo systemctl poweroff
exec sudo systemctl poweroff
elif [ "$2" -ge $HIBERN ] && sudo rtcwake -m disk -s $(($2 - $HIBERN / 2))
elif [ "$2" -ge $HIBERN ] && sudo rtcwake -m disk -s $(($2 - $HIBERN / 2))
Line 186: Line 218:
elif [ "$2" -ge "${FALLBACK_SHUTDOWN_DELAY:-}" ]
elif [ "$2" -ge "${FALLBACK_SHUTDOWN_DELAY:-}" ]
then
then
sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
#sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
exec sudo systemctl poweroff
exec sudo systemctl poweroff
fi
fi

Revision as of 07:33, 4 May 2023

Introduction

The main aim of systemd (a "system and service manager") is to unify service configuration and behavior across Linux distributions. It bootstraps the user space and manages user processes. It also provides replacements for various daemons and utilities, including device management, login management, network connection management, and event logging.

Since 2015, the majority of Linux distributions have adopted systemd, having replaced other init systems such as SysV init.

Making Systemd start up VDR

Before systemd, you might have edited /etc/gettydefs to prevent a virtual console from being associated with a normal login prompt, and then have init invoke a shell script that would invoke VDR, often named runvdr. With systemd, all you need is a single file, say, /etc/systemd/system/vdr.service, with contents like the following:

[Unit]
Description=Video Disk Recorder
After=systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service
After=getty@tty1.service
Conflicts=getty@tty1.service
Conflicts=shutdown.target
ConditionPathExists=/video/video

[Service]
User=pi
#ExecStartPre=+/etc/systemd/system/vdr-keep-alive.sh start
ExecStart=/usr/local/bin/vdr --no-kbd --lirc=/dev/lirc0 -Prpihddevice -v /video/video -s /var/lib/vdr/vdr-shutdown.sh
TimeoutStartSec=infinity
Type=idle
Restart=on-failure
RestartSec=1s
TTYVTDisallocate=yes
[Install]
WantedBy=display-manager.service

Adjustments

  • User= refers to the user account that will run the VDR service.
  • ExecStart= must refer to the full VDR invocation.
  • ConditionPathExists= is for the directory that contains the recordings, matching the -v parameter in ExecStart. If the recordings cannot be mounted, the service would not start up.

The shutdown script (named /var/lib/vdr/vdr-shutdown.sh in the above example) could do at least one of the following:

  • Adjust rtcwake (or Nvram_wakeup) to have the system start up automatically on the next scheduled recording.
  • Invoke sudo systemctl stop vdr or sudo systemctl halt to shut down VDR or the entire system. This will also terminate the shell that runs the script!

Installation

sudo systemctl enable vdr
sudo systemctl start vdr

or just (according to man systemctl):

sudo systemctl enable --now vdr

Example: Auto-starting VDR with swappable video storage

On a small system like the Raspberry_Pi, the internal storage is just large enough for the basic installation and system configuration. For recordings, you might want to use a USB-powered HDD or SSD.

You might make a virtue out of necessity and implement swappable video directories. One USB SSD might not be large enough for your entire collection of recordings. So, why not implement swappable storage, with the following user interface?

  1. When you plug in the USB cable (or it is attached on system startup), VDR will start up automatically.
  2. When you press the power button, VDR will shut down and the USB storage will be powered off, so that it can be safely unplugged.
  3. When you plug in the USB cable again, VDR will start up again. This could be a different drive.

Preparations

Create a file system and label it VDR. Create the subdirectory video inside it.

Below, we assume that the storage devices that you want to initialize is /dev/sdd. Substitute the correct name for sdd below, and ensure that you are accessing the right device!

sudo mkfs.ext4 /dev/sdd1
sudo tune2fs -L /dev/sdd1
sudo mount /dev/sdd1 /mnt
mkdir /mnt/video
sudo umount /mnt

On the VDR system, create a mount point for the storage devices that have been prepared as above.

sudo mkdir -m 000 /video

Automatically mounting storage when it is plugged in

You might think of a simple /etc/fstab entry like this:

LABEL=VDR /video ext4 defaults,noatime,nofail 0 1

But, we want the file system to be mounted automatically when it is labeled VDR. To achieve that, we create a file /etc/systemd/system/video.mount (the name must match the mount point /video):

[Unit]
BindsTo=dev-disk-by\x2dlabel-VDR.device
After=dev-disk-by\x2dlabel-VDR.device
Requires=systemd-fsck@dev-disk-by\x2dlabel-VDR.service
After=systemd-fsck@dev-disk-by\x2dlabel-VDR.service

[Mount]
Where=/video
What=/dev/disk/by-label/VDR
Type=ext4
Options=defaults,noatime,nofail

[Install]
WantedBy=dev-disk-by\x2dlabel-VDR.device

The magic .device unit refers to the path /dev/disk/by-label/VDR, which will become available when a device containing a file system labeled VDR is attached. We use that name also in the What= directive.

Now we are ready to enable the unit:

sudo systemctl enable video.mount

To check the status, you may find the following commands useful:

systemctl status
journalctl -u video.mount
journalctl -xe

Automatically starting VDR when the storage is plugged in

We also want VDR to start automatically once the file system has been mounted. To do that, the following lines have to be added to /etc/systemd/system/vdr.service:

[Install]
WantedBy=dev-disk-by\x2dlabel-VDR.device

If you had the following lines at the end of the file, they can be removed:

[Install]
WantedBy=display-manager.service

To notify systemd of the update:

sudo systemctl reenable vdr

Preventing shutdown and reboot while VDR is running

By default, the power button on a remote control that is managed by a kernel LIRC driver will be mapped to systemd-logind. Unless you have overridden the default HandlePowerKey=poweroff in /etc/systemd/logind.conf, the system would be shut down immediately when you hit a power button on any input device (remote control, keyboard, or power button on the computer case).

Often, VDR is the main application on the system. You would not want the system to be accidentally shut down or rebooted while someone is watching TV, or a recording is in progress. System shutdown would be initiated by a script that would be invoked by VDR itself; see the -s parameter of ExecStart above.

Policykit script

Policykit after version 105 should support Javascript based rules. Older versions only supported a .pkla format. The place for custom user rules is the directory /etc/polkit-1/rules.d/.

Create the file /etc/polkit-1/rules.d/50-vdr-prevent-shutdown.rules with the following contents:

const power_actions = [
  "org.freedesktop.login1.reboot",
  "org.freedesktop.login1.reboot-multiple-sessions",
  "org.freedesktop.login1.suspend",
  "org.freedesktop.login1.suspend-multiple-sessions",
  "org.freedesktop.login1.hibernate",
  "org.freedesktop.login1.hibernate-multiple-sessions",
  "org.freedesktop.login1.power-off",
  "org.freedesktop.login1.power-off-multiple-sessions"
];

polkit.addRule (function (action, subject) {
  if (power_actions.includes(action.id)) {
    try {
      polkit.spawn(["/usr/bin/test", "!", "-d", "/video/video"]);
        return polkit.Result.YES; 
    }
    catch (error) {
      return polkit.Result.NO; 
    }
  }
});

This will prevent the power actions as long as the directory /video/video exists, which could mean that a detachable video storage is plugged in. You may want to replace the polkit.spawn call with something else.

Commands like systemctl poweroff -i will work, because the script does not cover actions like org.freedesktop.login1.power-off-ignore-inhibit.

Old way: dynamically reconfiguring systemd

You might use the following script, say, /etc/systemd/system/vdr-keep-alive.sh. You could edit the TARGETS below to include suspend, hibernate, or others mentioned in man 7 systemd.special. Executing the script will require super user privileges.

#!/bin/sh

TARGETS="
/lib/systemd/system/poweroff.target.d
/lib/systemd/system/reboot.target.d
/lib/systemd/system/halt.target.d
"
CONF=vdr-keep-alive.conf

case "$1" in
start)
  for t in $TARGETS
  do
    if [ ! -f "$t/$CONF" ]
    then
      if [ ! -d "$t/" ]
      then
        mkdir "$t"
      fi
      echo "[Unit]\nRefuseManualStart=yes" > "$t/$CONF"
    fi
  done
  ;;
stop)
  for t in $TARGETS
  do
    rm -f "$t/$CONF"
  done
  ;;
esac

exec systemctl daemon-reload

You can execute this script as follows:

sudo /etc/systemd/system/vdr-keep-alive.sh start
sudo /etc/systemd/system/vdr-keep-alive.sh stop

Shutting down VDR

Last, we need a VDR shutdown script /var/lib/vdr/vdr-shutdown.sh that will power off the storage so that it can be safely detached, or shut down the system and schedule a restart for the next timer:

#!/bin/sh
#exec >> /var/tmp/vdr-shutdown.txt 2>&1; set -x; : "$@"
set -eu
HIBERN=120 # time in seconds to suspend to disk + resume from it
#FALLBACK_SHUTDOWN_DELAY=86400 # seconds
#STORAGE=/dev/disk/by-label/VDR
[ -n "${STORAGE:-}" ] && sudo systemd-mount -u "$STORAGE"
if [ "$5" = 1 -a -n "${STORAGE:-}" ]
then
  sudo rtcwake -m no -s "$2" || :
  #sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
  exec sudo udisksctl power-off -b "$STORAGE"
elif [ "$2" = 0 ]
then
  sudo rtcwake -m disable || :
  #sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
  exec sudo systemctl poweroff
elif [ "$2" -ge $HIBERN ] && sudo rtcwake -m disk -s $(($2 - $HIBERN / 2))
then
  :
elif sudo rtcwake -m mem -s "$2"
then
  :
elif [ "$2" -ge "${FALLBACK_SHUTDOWN_DELAY:-}" ]
then
  #sudo sh /etc/systemd/system/vdr-keep-alive.sh stop
  exec sudo systemctl poweroff
fi
[ -n "${STORAGE:-}" ] && sudo systemd-mount "$STORAGE"

Wake on timer

The script relies on the versatile command rtcwake for wake-on-timer control.

You may want to replace the #exec line with exec to enable debug output to the file /var/tmp/vdr-shutdown.txt. This file should survive system shutdown and restart.

Adjust HIBERN to the time it takes to suspend to disk and resume from it. If this fails because no swap space has been configured, the script will fall back to suspend-to-RAM.

If there is no real-time clock device for scheduling wake-up (say, on a Raspberry Pi), all rtcwake commands will fail. It would be the user's responsibility to shut down or wake up the system between recordings (or leave it running all the time). You might want to set FALLBACK_SHUTDOWN_DELAY at the start of the script to have an automatic shutdown (followed by manual wakeup) if the next scheduled recording is in distant enough future.

Detachable storage

When the line starting with #STORAGE= is commented out, there will be no difference between the user pressing the Power button, and the system being shut down due to an inactivity timeout.

If you want to use detachable video storage, uncomment the line and adjust the directory name if needed. The rest of this section assumes that you have done so.

If any process is using the STORAGE mount point, the system will refuse to shut down for any reason (inactivity timeout, or the Power button being pressed). In VDR, an active recording or the playback of a recording (even a paused playback) counts as using the mount point.

If unmounting the STORAGE succeeds, and the Power button was pressed, the line starting with if [ "$5" = 1 will power off the storage and shut down VDR. The rest of the system will remain powered on. The power LED indicator on a USB storage device will become an indicator of two things:

  • whether it is safe to disconnect the cable; you can plug it in again to have VDR start up (on same or different drive)
  • whether shutdown is possible, by pressing the Power button one more time

Some systems are unable to wake up after power-off; see rtcwake for details. On an affected system, you might want to remove the section starting with if [ "$5" = 1 to let the power button press lead directly to suspend-to-disk or suspend-to-RAM, just like an inactivity timeout would.

Notes

You can still execute the following commands to stop or start the VDR service, for example, for upgrading VDR or plugins:

sudo systemctl stop vdr
sudo systemctl start vdr

If you use these commands, the storage device will remain mounted in the file system, and the status of the reboot and shutdown targets will not change.