Yesterday, I finally bought external storage for my Raspberry Pi based VDR setup, a Samsung Portable SSD T7. It supports USB 3, but it also works on the Raspberry Pi 2's USB 2.0 and does not consume too much power. My old tower PC case based system that I had set up in 2004 has now been replaced with something that is better in every thinkable respect: power consumption, noise (passive cooling, no HDDs), speed, and size (not much larger than the remote control unit).
Hardware: * Raspberry Pi 2 * Pi TV hat * TV hat case * an IR receiver attached via soldered wires to the TV hat, at GPIO pin 18 * a remote control unit (from an old Hauppauge Nova-T PCI card) * Samsung Portable SSD T7 (1 TB)
Software: * Raspberry OS Legacy installed on a MicroSD card, with no GUI * sudo apt install ir-keytable * VDR 2.6.3 (or 2.6.2) compiled from source * https://github.com/reufer/rpihddevice/ compiled from source * "make install" to /usr/local
My /boot/config.txt includes the following lines: dtoverlay=gpio-ir,gpio_pin=18 dtparam=audio=on gpu_mem=256
In /etc/rc_maps.cfg (the configuration file of ir-keytable), ensure that there is a line like the following that will match the remote control unit that you are using: * * hauppauge.toml The above works for several RC5 based Hauppauge remote control units.
To prevent the Power button on the remote control unit from shutting down the entire system, add the following to the [Login] section of /etc/systemd/logind.conf:
HandlePowerKey=ignore
The default is HandlePowerKey=poweroff.
Use mkfs.ext4 to replace the FAT file system of the only partition of the T7. There is no need to change the partitioning or specify a block size or alignment, because the physical block size is reported as 512 bytes. Optionally, you may set a label by executing something like this: tune2fs -L VDR /dev/sda1
You may create a mount point:
sudo mkdir -m 000 /video
Then, add a line like this to /etc/fstab to have the storage mounted automatically:
LABEL=VDR /video ext4 defaults,noatime,nofail 0 1
You may replace the LABEL=VDR with whatever symbolic link you have in /dev/disk/by-label (see also tune2fs above). On my system, I actually wrote PARTUUID=33d32895-01 because there is a symbolic link /dev/disk/by-partuuid/33d32895-01 that identifies the partition.
Once the storage is mounted, execute the following: sudo mkdir /video/video sudo chown pi:pi /video/video
The next step is to configure VDR to start up correctly. I have some configuration files in /var/lib/vdr. For testing, I used to start VDR manually from the command line, and shut it down by choosing "restart" from the OSD menu. Now I want it to restart automatically, but only if suitable USB storage has been plugged in:
sudo tee /etc/systemd/system/vdr.service << EOF [Unit] After=systemd-user-sessions.service plymouth-quit-wait.service After=rc-local.service After=getty@tty1.service After=video.mount Conflicts=getty@tty1.service
ConditionPathExists=/video/video
[Service] User=pi 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] Alias=display-manager.service EOF
This will replace the getty process on virtual terminal 1 (tty1). If the storage is not plugged within 90 seconds from startup (I do not know how to configure that timeout), then an error message will appear on the console. No getty will be started on tty1 in any case; you can always log in from tty2 by pressing Alt+F2.
The shutdown script /var/lib/vdr/vdr-shutdown.sh does not work as intended yet:
#!/bin/sh
if [ "$5" = 1 ] then sudo service vdr stop sudo umount /video sudo udisksctl power-off -b /dev/sda fi
The first step appears to terminate the shell script, because the shell is a subprocess of VDR. So, the storage will remain mounted and powered on. I guess that we need to launch a separate "vdr-shutdown" service that would take care of the remaining steps. Has someone already implemented something like this?
After the "umount" and "udisksctl" commands are executed, it is safe to unplug the storage. The LED of the SSD will shortly change color and then turn off during the execution of the "udisksctl" command.
What I am also missing is a udev rule that would automatically mount the storage and attempt to start up VDR as soon as the storage is plugged in. Currently, I have to manually execute the following if I plug in the drive to an already running system:
sudo mount /video sudo service vdr start
This configuration provides a rather simple user interface for VDR. No keyboard or mouse is needed, just the remote control unit, a display, and optionally the USB cable, if the system has other uses that are independent of VDR.
For timed recordings, I think that on the Raspberry Pi, it is easiest to let the VDR process run all the time. Starting up the Pi based on timer would require additional hardware.
One thing that I'd like to improve in that regard is to let VDR shut down all tuners when the system is idle. This could be based on an inactivity timer or an explicit user action, such as pressing a button on the remote control. The video output would shut off, and the tuner would be powered off, except when needed for something (EPG scan, recording). As soon as a button is pressed on the remote control unit, the user interface would spring back to life.
Happy holidays,
Marko
On 24.12.22 10:33, Marko Mäkelä wrote:
then sudo service vdr stop sudo umount /video sudo udisksctl power-off -b /dev/sda fi
The first step appears to terminate the shell script, because the shell is a subprocess of VDR. So, the storage will remain mounted and powered on. I guess that we need to launch a separate "vdr-shutdown" service that would take care of the remaining steps. Has someone already implemented something like this?
Maybe you can prevent that by backgrounding and setting some bash trap's on SIGINT or SIGKILL.
Other methods to decouple a process so it runs separated, would be 'nohup' or the old 'at now' trick.
However, I'd say the most clean way is to include the unmount and udiskctrl into the vdr shutdown itself, so these commands run within the vdr service after the vdr process stops. That way you just have to fire the service stop and are done.
From a quick look into documentation, you can just not wait for the stop to complete:
systemctl stop vdr.service --no-block
Or if you want to go full systemd, you can probably do a service that does the mount/unmount/poweroff on start/stop, and then add that as requirement to the vdr service.
Cheers,
Udo
Sun, Dec 25, 2022 at 01:10:51PM +0100, Udo Richter wrote:
On 24.12.22 10:33, Marko Mäkelä wrote:
then sudo service vdr stop sudo umount /video sudo udisksctl power-off -b /dev/sda fi
The first step appears to terminate the shell script, because the shell is a subprocess of VDR. So, the storage will remain mounted and powered on. I guess that we need to launch a separate "vdr-shutdown" service that would take care of the remaining steps. Has someone already implemented something like this?
Maybe you can prevent that by backgrounding and setting some bash trap's on SIGINT or SIGKILL.
All these steps depend on each other: VDR must be stopped before the video directory can be unmounted, and actually any pending writes should be durably submitted before the device is powered off. That would make any asynchronous invocation a little more challenging.
I did not test it carefully yet, but I understood from the systemd documentation that the following might work as intended (umount as soon as all file handles are closed by VDR):
sudo systemd-umount /video sudo service vdr stop
I just learned about "man systemd.mount" via this question on automating backups to USB storage: https://unix.stackexchange.com/questions/89881/
However, I'd say the most clean way is to include the unmount and udiskctrl into the vdr shutdown itself, so these commands run within the vdr service after the vdr process stops. That way you just have to fire the service stop and are done.
Do you mean implementing that as part of VDR plugin?
Or if you want to go full systemd, you can probably do a service that does the mount/unmount/poweroff on start/stop, and then add that as requirement to the vdr service.
A simple change to the shutdown script would be to have it execute something like "sudo shutdown -h now" (or whatever invocation systemd prefers). That will umount and power off the USB storage just fine.
I think that I might actually go down that route, because currently the Raspberry Pi runs no other services than VDR. Except for the -s script, the vdr.service file that I posted yesterday already works for me.
I did not find any systemd integration with udisksctl, other than as part of an implicit poweroff service.
I could also avoid defining a shutdown script at all, and instead define a command script for invoking shutdown. That could be invoked when there are no timed recordings coming up for a while. I would have to remember to power up the system manually for the next timed recording. This would allow the power button on the remote control to be mapped to a "suspend" function that would make VDR stop the video output and stop polling the tuner. The USB SSD could remain powered on; I do not think it will consume much power when not being accessed.
Marko
On 25.12.22 20:47, Marko Mäkelä wrote:
However, I'd say the most clean way is to include the unmount and udiskctrl into the vdr shutdown itself, so these commands run within the vdr service after the vdr process stops. That way you just have to fire the service stop and are done.
Do you mean implementing that as part of VDR plugin?
No, just replace the call to vdr in the service with a call to a runvdr script (any of the ones floating around, or just a three-liner), and in that script, after vdr ends, do whatever cleanup you need to do.
Cheers,
Udo
Mon, Dec 26, 2022 at 01:34:48AM +0100, Udo Richter wrote:
No, just replace the call to vdr in the service with a call to a runvdr script (any of the ones floating around, or just a three-liner), and in that script, after vdr ends, do whatever cleanup you need to do.
I see. That could certainly work.
I could execute "udisksctl unmount -f /dev/disk/by-label/VDR" to have the file system lazily unmounted as soon as all files are closed. But there is no such lazy option for power-off, and power-off itself will not unmount the file system.
Because I store the VDR configuration outside of this file system, it should be safe to attempt to unmount the video directory while VDR is running:
#!/bin/sh set -e if [ "$5" = 1 ] then sudo udisksctl unmount -b /dev/disk/by-label/VDR sudo udisksctl power-off -b /dev/disk/by-label/VDR sudo service vdr stop fi
I tested that the above script works when I press the Power button while watching live TV. If I am watching a recording that resides on this file system, the first command will fail, and thanks to "set -e", the rest of the script (in particular, shutting down the VDR service) will be skipped, that is, VDR will keep running.
In addition to having this script, I might configure some more, such as:
* Write some udev rule so that when the USB storage is unplugged and replugged, the file system will be auto-mounted and VDR service will be started. * Restore /etc/systemd/logind.conf to HandlePowerKey=poweroff so that the system can be easily shut down by pressing the Power button again.
That would make it easy to do things like moving the disk to another computer for backing up or managing recordings. (Yes, I would rather use the native speed of the drive via USB 3, rather than have it throttled by 100 Mb/s Ethernet or the single USB 2.0 bus of the Raspberry Pi 2.)
In fact, this type of setup would also allow easy swapping of USB drives (one at a time) for making or watching recordings, a little like it was in the VCR times. The downtime for swapping drives would be a few seconds. The VDR configuration would be stored in the root file system, outside these detachable drives. The user interface to the entire setup would remain "living room compliant": remote control, display, and the USB cable of the drive.
The system could keep powering other tasks (such as home automation) while no storage is attached. If I didn't care about downtime, I might also use the following crude script to simply shut down the entire system:
#!/bin/sh if [ "$5" = 1 ] then sudo shutdown -h now fi
In either case, the user would be responsible for starting VDR in time for the next scheduled recording.
Marko
Tue, Dec 27, 2022 at 12:04:00PM +0200, Marko Mäkelä wrote:
I might configure some more, such as:
- Write some udev rule so that when the USB storage is unplugged and
replugged, the file system will be auto-mounted and VDR service will be started.
- Restore /etc/systemd/logind.conf to HandlePowerKey=poweroff so that
the system can be easily shut down by pressing the Power button again.
I finally figured it out mostly. What is missing is a nice way of overriding the power button. When VDR is running, the button should be handled by VDR; otherwise, pressing the power button should trigger system shutdown, like it does by default.
First, I removed the custom /etc/fstab entry. Everything will be controlled by systemd as follows:
sudo mkdir -m 000 /video sudo tee /etc/systemd/system/video.mount << "EOF" [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 EOF sudo tee /etc/systemd/system/vdr.service << "EOF" [Unit] Description=Runs VDR when storage is plugged in After=systemd-user-sessions.service plymouth-quit-wait.service After=rc-local.service After=getty@tty1.service BindsTo=dev-disk-by\x2dlabel-VDR.device After=dev-disk-by\x2dlabel-VDR.device After=video.mount Conflicts=getty@tty1.service ConditionPathExists=/video/video [Service] User=pi 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=dev-disk-by\x2dlabel-VDR.device EOF sudo systemctl enable video.mount sudo systemctl enable vdr sudo udevadm control --reload cat > /var/lib/vdr/vdr-shutdown.sh << "EOF" #!/bin/sh set -eu if [ "$5" = 1 ] then sudo udisksctl unmount -b /dev/disk/by-label/VDR sudo udisksctl power-off -b /dev/disk/by-label/VDR PK=/etc/systemd/logind.conf.d/00-powerkey.conf sudo mv -f "$PK".disabled "$PK" || : sudo systemctl restart systemd-logind sudo mv -f "$PK" "$PK".disabled sudo systemctl stop vdr fi EOF chmod +x /var/lib/vdr/vdr-shutdown.sh sudo mkdir /etc/systemd/logind.conf.d sudo tee /etc/systemd/login.conf.d/00-powerkey.conf.disabled << "EOF" [Login] HandlePowerKey=poweroff EOF sudo systemctl restart systemd-logind
In addition to these, /etc/systemd/login.conf needs to be edited so that its [Login] section says HandlePowerKey=ignore.
I also had a udev rule that would execute "systemctl restart systemd-logind" when the storage is plugged in. Without that, if I plugged in the storage (and got VDR auto-started), then pressed the power button, and then repeated the same (unplug, replug, press the power button again), the system would shut down abruptly.
If there is a better way to make systemd-logind ignore the power button as long as VDR is running, please let me know. I tried adding Conflicts=exit.target to the vdr.service, but that did not prevent the shutdown. A rule Conflicts=shutdown.target means that the service shall be terminated on shutdown, according to "man 7 systemd.special".
Marko
Am 27.12.22 um 21:49 schrieb Marko Mäkelä:
First, I removed the custom /etc/fstab entry. Everything will be controlled by systemd as follows:
On systemd-systems, each line in /etc/fstab is automatically converted by a binary "systemd-fstab-generator" into "xxx.mount" units. So what you do here
sudo tee /etc/systemd/system/video.mount << "EOF" [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 EOF
is more or less the same as one line in /etc/fstab.
see "man systemd.mount" and "man systemd-fstab-generator"
Just a hint....
Martin
Tue, Dec 27, 2022 at 11:15:29PM +0100, Martin Dummer wrote:
Am 27.12.22 um 21:49 schrieb Marko Mäkelä:
First, I removed the custom /etc/fstab entry. Everything will be controlled by systemd as follows:
On systemd-systems, each line in /etc/fstab is automatically converted by a binary "systemd-fstab-generator" into "xxx.mount" units.
That is correct. I converted the fstab-generated video.mount file to a custom one.
So what you do here
[...]
is more or less the same as one line in /etc/fstab.
It is, except for the [Unit] keys BindsTo and After, and the [Install] key WantedBy, which causes the file system to be mounted automatically and the dependent service started once a matching storage device is plugged in.
Years ago, executing commands when USB storage was plugged in could be achieved by writing udev rules. In fact, I first attempted that, but I did not come up with a solution that would work both on system startup and when the storage is plugged into an already running system. The systemd mount unit and the service unit, with declared dependencies on /dev/disk/by-label/VDR do exactly what I need, without being tied to a specific USB ID.
When it comes to the power button, I found a Python 2.7 script https://github.com/ryran/reboot-guard that can disable or enable shutdown, reboot, halt, by creating or removing simple configuration files. Because this script is not packaged in Debian, and Python 2 is kind of obsolete, I wrote a shell script to achieve the same:
sudo tee /etc/systemd/system/vdr-keep-alive.sh << "EOF" #!/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 EOF
Whether the override is in place can be checked with commands like "systemctl cat shutdown.target". On my system, "sudo reboot" would be silently ignored, while "sudo systemctl reboot" would display verbose messages, suggesting to check "systemctl status reboot.target" for details.
Furthermore, I learned about ExecStartPre and ExecStop. The former can be used for preparatory steps, such as blocking the shutdown and reboot commands with the above script. ExecStop seems to be unusable for this type of a service. I moved those steps to be part of the shutdown script that will be invoked by VDR:
tee /var/lib/vdr/vdr-shutdown.sh << "EOF" #!/bin/sh set -eu if [ "$5" = 1 ] then sudo systemd-mount -u /dev/disk/by-label/VDR sudo /etc/systemd/system/vdr-keep-alive.sh stop sudo udisksctl power-off -b /dev/disk/by-label/VDR fi EOF
Thanks to the declared dependencies, the udisksctl command will actually stop VDR.
The "set -e" at the start of the script ensures that if the file system cannot be unmounted (due to VDR playing back a recording, or any other process accessing the file system), the script will be aborted at the first step, and VDR will keep running.
Finally, here is the revised service file. It now includes Conflicts=shutdown.target to ensure an orderly shutdown of VDR in case the logic to disable shutdown is disabled:
sudo tee /etc/systemd/system/vdr.service << "EOF" [Unit] Description=Video Disk Recorder After=systemd-user-sessions.service plymouth-quit-wait.service After=rc-local.service After=getty@tty1.service BindsTo=dev-disk-by\x2dlabel-VDR.device After=dev-disk-by\x2dlabel-VDR.device After=video.mount 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=dev-disk-by\x2dlabel-VDR.device EOF
With this setup, any USB based storage that contains a partition that contains a file system labeled VDR that contains the subdirectory "video" becomes special as follows:
1. The first virtual console (/dev/tty1) is always reserved for VDR. 2. If the storage is not available on startup, there will be no timeout or waiting (like with /etc/fstab there would be). 3. If the storage is available on startup or plugged in at any later time, it will be mounted in /video. If /video/video exists, VDR will be started and the shutdown, reboot, halt actions disabled. 4. If VDR is shut down from its user interface, the file system will be unmounted, the storage powered off, and the shutdown, reboot, halt actions re-enabled.
The drive LED becomes a proxy indicator for "VDR is running"; there is no need to turn on the screen to check that.
To restart VDR, just unplug the USB cable (after the USB drive LED has turned off) and plug the cable back in, optionally using a different drive.
To shut down the entire system, simply press the Power button once more while the USB drive LED is off (or the drive is disconnected).
In my old PC based system, I had an ATtiny microcontroller board that would run on standby power, listen to the IR receiver, and start up the system via WoL or PCI PME when the power button was pressed. While something similar would be possible with the Raspberry Pi (using GPIO 3, or pin 5 on the connector), I think that the current setup is good enough for me, with the IR receiver mounted on the TV HAT with 3 loose wires and poking out of the Pi TV HAT case. With a daughter board for the IR receiver and an SMD microcontroller using 4 loose wires, it should be doable.
Marko
Sat, Dec 24, 2022 at 11:33:19AM +0200, Marko Mäkelä wrote:
Yesterday, I finally bought external storage for my Raspberry Pi based VDR setup, a Samsung Portable SSD T7.
I have now documented my setup in the following wiki pages: https://www.linuxtv.org/vdrwiki/index.php/Systemd https://www.linuxtv.org/vdrwiki/index.php/LIRC https://www.linuxtv.org/vdrwiki/index.php/Raspberry_Pi
Most of it is not specific to the Raspberry Pi. I made the systemd article modular. Adding the pluggable video storage to the vdr.service file is only 2 lines (the [Install] section).
Marko