Night owl

Night owl

Building a MythTV PVR

How to (or in some cases - not to) do it

Introduction - the project
System components
Basic installation
HDMI monitor and overscan
Shutdown and wakeup
MythTV backend setup
MythTV frontend setup
Kodi setup
Conclusions and summary
Epilogue - 3 years on
Links

Shutdown and wakeup

For me, one of the key attributes for a PVR is that it must sit quietly in a shutdown state, wake up for recordings, then go back to sleep. Some people are happy for a system to remain fully powered 24 hours per day, but I'm not - it is a waste of energy for one thing.

There is a tremendous amount of useful information on this in the MythTV ACPI page, and also in the ACPI wiki page.

MythTV does support this operation, but since there are so many flavours of Linux and hardware capabilities, it just provides the hooks and leave the user to interface it to his or her system. To do this the backend can run three user provided scripts or commands:

  1. Shutdown check
  2. Set wake up time
  3. Shutdown or suspend

These are all on the shutdown and wakeup pages of the backend setup.

The shutdown check is simply a script that the backend will call when it wants to shutdown, and depending on the return code from the script, it either will or it won't. Set wake time calls a script with a parameter specifying when the system is to wake up again. Shutdown or suspend calls a script or command to carry out the actual shutdown. Note that even if you have all this set up and working, it will only operate if the backend instigates the shutdown - if you just shut the system down manually, it will stay shutdown. The process therefore is that the backend will call the check, and if is is OK to proceed it will call the set time and shutdown scripts in quick succession. If the backend is busy doing something like recording, it will not even start the process.

Most motherboards these days support the ACPI standard for power control. Machine states are given codes, G0 (fully operating) to G3 (off), and G1 (sleep) is divided into S numbers, for instance S3 is suspended to RAM, S4 to disk. G2 (or S5) is soft powerdown, ie off, requires a full reboot, but can automatically wake up on command. What we want to do here is put the machine into S5 when it is not wanted, so the first question is - does your motherboard support that?

Testing the motherboard capabilities

The MythTV ACPI page recommends checking what the log says, and for Ubuntu the way of doing this is to run the command sudo grep -i rtc /var/log/dmesg (looks for log lines with 'rtc' in them, case insensitive). I did this and it said the module was called rtc-cmos (good) and the system could wake from S4 (not so good, that is suspend to disk, not soft powerdown). However actually it appears that most motherboards can wake from S5 even if they say they cannot. The next thing to do is carry out some tests. To do this I wrote a script mythtv-reboottest which was placed in /usr/local/bin and made executable:

#!/bin/bash LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" DELAYTIME=30 if [ "$#" -gt 0 ] ; then DELAYTIME=$1 fi echo "" >> $LOGFILE echo "`date`" " : Running reboot test ($DELAYTIME seconds)" >> $LOGFILE CURTIME=`date +%s` REBOOTTIME=$((CURTIME + DELAYTIME)) echo "Times : $CURTIME / $DELAYTIME / $REBOOTTIME" >> $LOGFILE echo 0 > /sys/class/rtc/rtc0/wakealarm echo "$REBOOTTIME" > /sys/class/rtc/rtc0/wakealarm cat /proc/driver/rtc >> $LOGFILE echo "`date`" " : Shutting down now" >> $LOGFILE shutdown -P now

Note that one thing you do need to know is whether your hardware clock is local time or UTC. Mine is UTC which is fine, but if local you need to do some extra work in the script to convert from one to the other. (The way I checked was to add a line into /etc/init/hwclock-save.conf to echo $UTC to a logfile).

This script takes as a parameter a time delay in seconds (default 30 with nothing specified) after which the system should restart. Well, it didn't, and nothing I could do would make it restart. There are a lot of suggestions to try in the MythTV ACPI page, eg RTC in BIOS on and off, disabling HPET, altering hwclock-save.conf etc, and so on, but nothing would make it wake up again. Everything got set correctly and checked out, but it would not restart. It appears this motherboard just will not do it. (Note that the test delay needs to be around 5 minutes, shorter times often do not work at all on any motherboard).

Incidently, it appears that the changes as detailed in the MythTV ACPI page to hwclock-save.conf do not work as written: by the time the exec hwclock ... line has been executed, anything stored in $ACPITIME is lost. There are plenty of ways round that, but in fact I found that the wakeup time saved to /sys/class/rtc/rtc0/wakealarm didn't get erased anyway, so no changes were needed - but it still didn't restart.

Suspend to RAM instead

Another option is to make it suspend to a different state, RAM or disk, and wakeup from that. I did various tests on suspend to disk and it was unreliable - sometimes it did and sometimes it didn't - so that left suspend to RAM. Again various different approaches were tried using rtcwake, but the only one which worked reliably was the one suggested in the MythTV ACPI page - see S3 (Suspend to RAM) - where it is suspended to RAM, then on waking up immediately reboots (you probably do need to reboot to make sure all the hardware comes back cleanly). I modified it slightly, as follows:

  • The UTC time (Unix time in seconds since 1/1/1970) is written to a file /home/pvr/.mythtv/mythtv-waketime by the set waketime script
  • That file is read by the shutdown script, and possibly modified
  • The reboot script in /etc/init.d picks then it up, suspends, and reboots on wakeup

The scripts are:

/usr/local/bin/mythtv-setwaketime <time> - writes the desired wakeup time:

#!/bin/bash #sets the wake up time - parameter 1 is the UTC time (Unix seconds since 1/1/1970) LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" WAKEFILE="/home/pvr/.mythtv/mythtv-waketime" echo "" >> $LOGFILE echo "`date`" " : Setting wake time" >> $LOGFILE if [ $# -eq 0 ] ; then rm -f $WAKEFILE echo "No wake time given (any old waketime file deleted)" >> $LOGFILE exit 1 fi echo "Wake time: " "`date -d@$1` ($1)" "written to $WAKEFILE" >> $LOGFILE echo "$1" > $WAKEFILE exit 0

/usr/local/bin/mythtv-suspend [<mode> [<delay>]] - reads the waketime file and compares with another optional passed in delay (parameter 2), possibly updates the waketime file if the delay time is sooner, then shuts down or reboots depending on the mode set by parameter 1 (spd - soft powerdown, or str - suspend to RAM). However if there are no parameters, it just shuts down:

#!/bin/bash #suspends operation until sooner of the wake up time #(Unix time, UTC) or passed in time delay (seconds), or shuts down #if no wake time or time delay #param 1 waketime mode, spd (soft power down), str (suspend to RAM) or nothing (shutdown) #param 2 optional, time delay in seconds function haltuntilwakeup () { echo "haltuntilwakeup($1, $2, $3)" >> $4 mythtv-closekodi if [ "$1" == 1 ] ; then echo 0 > /sys/class/rtc/rtc0/wakealarm echo "$2" > /sys/class/rtc/rtc0/wakealarm cat /proc/driver/rtc >> $4 echo "`date`" " : Shutting down now" >> $4 rm -f $3 shutdown -P now else echo "`date`" " : Rebooting" >> $4 reboot fi } LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" WAKEFILE="/home/pvr/.mythtv/mythtv-waketime" CURTIME=`date +%s` LASTDATA=$(tail -n 100 /var/log/mythtv/hwclock-WakeTime.log) echo "$LASTDATA" > /var/log/mythtv/hwclock-WakeTime.log echo "" >> $LOGFILE echo "`date`" " : Suspend or shutdown" >> $LOGFILE SPD="" case $1 in spd) let "SPD = 1" ;; str) let "SPD = 0" ;; *) echo "No wakeup mode set - shutting down" >> $LOGFILE rm -f $WAKEFILE mythtv-closekodi shutdown -P now exit 0 ;; esac # wake time if present, should be in the wake file # (used once only, erased after use by shutdown script) WAKETIME=0 if [ -f $WAKEFILE ] ; then WAKETIME=`cat $WAKEFILE` fi if [ "$WAKETIME" -gt "$CURTIME" ] ; then echo "Wake time " "`date -d@$WAKETIME` ($WAKETIME), found in $WAKEFILE" >> $LOGFILE if [ "$2" -gt 0 ] ; then # there is a passed in delay (seconds) as well echo "Delay of $2 seconds found" >> $LOGFILE DELAYTIME=`expr $CURTIME + $2` if [ "$WAKETIME" -gt "$DELAYTIME" ] ; then # the delay is the sooner time to wake up, so use it echo "Delay time (sooner) " "`date -d@$DELAYTIME` ($DELAYTIME), writing to $WAKEFILE" >> $LOGFILE echo "$DELAYTIME" > $WAKEFILE let "WAKETIME = $DELAYTIME" fi fi haltuntilwakeup $SPD $WAKETIME $WAKEFILE $LOGFILE else # no wake file found, or nothing useful in it if [ "$2" -gt 0 ] ; then # assume $2 is the time delay in seconds required # (used for testing, and restart for EIT grabbing) echo "Delay of $2 seconds found" >> $LOGFILE WAKETIME=`expr $CURTIME + $2` echo "Delay time " "`date -d@$WAKETIME` ($WAKETIME), writing to $WAKEFILE" >> $LOGFILE echo "$WAKETIME" > $WAKEFILE haltuntilwakeup $SPD $WAKETIME $WAKEFILE $LOGFILE else # no time found, so just shutdown echo "No valid wake time found, shutting down" >> $LOGFILE rm -f $WAKEFILE mythtv-closekodi shutdown -P now fi fi exit 0

/usr/local/bin/mythtv-closekodi - it was noticed that when the above operated and shut down, if Kodi was running (and it is always running in the foreground at a shutdown), whilst it was idle at the time it always generated a crash log. Therefore the above calls this script to try to close Kodi tidily:

#!/bin/bash LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" #LOGFILE="/dev/stdout" LOOPLIMIT=10 echo "" >> $LOGFILE echo "`date`" " : Kodi shutdown" >> $LOGFILE KODIPID=$(pidof kodi.bin) if [ ! -z $KODIPID ] ; then echo "Kodi running (PID $KODIPID), requesting stop ..." >> $LOGFILE curl -s --data-binary '{"jsonrpc": "2.0", "method": "Application.Quit", "id": 1}' --> -H 'content-type: application/json;' http://127.0.0.1:8080/jsonrpc > /dev/null let "COUNT = 0" while [ ! -z $(pidof kodi.bin) ] ; do let "COUNT = $COUNT + 1" if [ $COUNT -gt $LOOPLIMIT ] ; then echo "Gave up waiting for stop, sending kill signal" >> $LOGFILE kill -SIGKILL $(pidof kodi.bin) break fi echo "Waiting for stop ..." >> $LOGFILE sleep 1 done else echo "Kodi not running" >> $LOGFILE fi

/etc/init.d/mythtv-wakeup - this is linked into the system reboot process and run automatically. It suspends for the period in the waketime file, then on coming to life again, the system continues rebooting:

#!/bin/sh ### BEGIN INIT INFO # Provides: mythtv-wakeup # Required-Start: # Required-Stop: # Default-Start: # Default-Stop: 0 6 # Short-Description: Start NTP daemon ### END INIT INFO PATH=/sbin:/bin:/usr/sbin:/usr/bin . /lib/lsb/init-functions NAME=mythtv-wakeup case $1 in stop) LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" WAKEFILE="/home/pvr/.mythtv/mythtv-waketime" echo "" >> $LOGFILE echo "`date`" " : Shutdown script" >> $LOGFILE if [ -f $WAKEFILE ] ; then # wake time found WAKETIME=`cat $WAKEFILE` rm -f $WAKEFILE echo "Wake time " "`date -d@$WAKETIME` ($WAKETIME)" " found, suspending" >> $LOGFILE # Make it wakeup from alarm. echo 0 >/sys/class/rtc/rtc0/wakealarm echo $WAKETIME >/sys/class/rtc/rtc0/wakealarm cat /proc/driver/rtc >> $LOGFILE /usr/sbin/pm-suspend echo "`date`" " : woken up again" >> $LOGFILE else echo "No wake time found, not suspending" >> $LOGFILE fi ;; *) echo "Usage: $0 {stop}" exit 2 ;; esac

Running this was the reason pm-utils was installed. Note that for this script to be run, it must be set executable, and symbolic links to it from /etc/rc0.d and /etc/rc6.d must be created as follows (read about the Linux boot/shutdown process to find out how and when these scripts run):

ln -s /etc/init.d/wakeup /etc/rc0.d/S50wakeup ln -s /etc/init.d/wakeup /etc/rc6.d/S50wakeup

Power button shutdown

Finally, the other way of shutting down the system is with the power button, which causes a full shutdown in my case (no wake up restart). Therefore since Kodi is likely to be running when this is going on the same close Kodi mechanism as above may be desirable. In order to achieve that the /etc/acpi/powerbtn.sh script was cloned into a similar one /usr/local/bin/mythtv-powerbtn.sh that calls mythtv-suspend (with no parameters) rather than just using shutdown:

#!/bin/sh # /etc/acpi/powerbtn.sh # Initiates a shutdown when the power putton has been # pressed. Slight change to std one to close Kodi if running [ -r /usr/share/acpi-support/power-funcs ] && . /usr/share/acpi-support/power-funcs # If logind is running, it already handles power button presses; desktop # environments put inhibitors to logind if they want to handle the key # themselves. if pidof systemd-logind >/dev/null; then exit 0 fi # getXuser gets the X user belonging to the display in $displaynum. # If you want the foreground X user, use getXconsole! getXuser() { user=`pinky -fw | awk '{ if ($2 == ":'$displaynum'" || --> $(NF) == ":'$displaynum'" ) { print $1; exit; } }'` if [ x"$user" = x"" ]; then startx=`pgrep -n startx` if [ x"$startx" != x"" ]; then user=`ps -o user --no-headers $startx` fi fi if [ x"$user" != x"" ]; then userhome=`getent passwd $user | cut -d: -f6` export XAUTHORITY=$userhome/.Xauthority else export XAUTHORITY="" fi export XUSER=$user } # Skip if we just in the middle of resuming. test -f /var/lock/acpisleep && exit 0 # If the current X console user is running a power management daemon that # handles suspend/resume requests, let them handle policy This is effectively # the same as 'acpi-support's '/usr/share/acpi-support/policy-funcs' file. [ -r /usr/share/acpi-support/power-funcs ] && getXconsole PMS="gnome-settings-daemon kpowersave xfce4-power-manager" PMS="$PMS guidance-power-manager.py dalston-power-applet" PMS="$PMS mate-settings-daemon" PMS="$PMS unity-settings-daemon" if pidof x $PMS > /dev/null; then exit elif test "$XUSER" != "" && pidof dcopserver > /dev/null && --> test -x /usr/bin/dcop && /usr/bin/dcop --user $XUSER kded kded --> loadedModules | grep -q klaptopdaemon; then exit elif test "$XUSER" != "" && test -x /usr/bin/qdbus; then kded4pid=$(pgrep -n -u $XUSER kded4) if test "$kded4pid" != ""; then dbusaddr=$(su - $XUSER -c "grep -z DBUS_SESSION_BUS_ADDRESS --> /proc/$kded4pid/environ") if test "$dbusaddr" != "" && su - $XUSER -c "export $dbusaddr; --> qdbus org.kde.kded" | grep -q powerdevil; then exit fi fi fi # If all else failed, just initiate a plain shutdown. mythtv-suspend

and then call this in /etc/acpi/events/powerbtn in place of /etc/acpi/powerbtn.sh (though note there are various other ways out of the power button script):

# /etc/acpi/events/powerbtn # This is called when the user presses the power button and calls # /etc/acpi/powerbtn.sh for further processing. # Optionally you can specify the placeholder %e. It will pass # through the whole kernel event message to the program you've # specified. # We need to react on "button power.*" and "button/power.*" because # of kernel changes. event=button[ /]power #action=/etc/acpi/powerbtn.sh action=/usr/local/bin/mythtv-powerbtn.sh

Various points:

  • This is all a bit messy, all could be rewritten as they are the result of a lot of modification and experimentation to get it all to work
  • The parameter 1 in the suspend script to set the mode does indicate that I did get soft power down to work in the end - but not with this motherboard

Using the Gigabyte GA-MA78GM-S2H motherboard

All this worked, so it was a success. However it does have one big downside compared to soft powerdown. The issue is that the system spends a lot of time suspended to RAM. If these is a power failure (we get a lot of glitches - 2 or 3 second outages), it then reboots, and because of the BIOS powerup setting it does restart. However the system detects an error (incorrect shutdown) and creates a crash log which it prompts you with on reboot. Probably, given that it was suspended at the time, no corruption is likely, but it is not ideal. There were several ways round this:

  1. Get a motherboard that does support soft powerdown
  2. Get a small UPS so power glitches have no effect
  3. Try another approach - suspend to disk might restart cleanly if it could be sorted out why it is unreliable, or there is a utility for writing directly to the BIOS NV RAM (see http://manpages.ubuntu.com/manpages/precise/man8/nvram-wakeup.8.html, so maybe the time could be set that way
  4. Maybe I missed a trick and the ASRock motherboard does support restart from soft powerdown

In a way, the UPS is probably best (you don't want a power failure when it is recording, either). It is possible that with more experimentation some approach in option 3 or 4 might work. However, I got hold of a secondhand Gigabyte GA-MA78GM-S2H motherboard with CPU and RAM and used that instead. This one had no problem restarting from soft powerdown, so that is what is in the system now, and the ASRock motherboard has been moved to other duties.

Much easier, tests with mythtv-reboottest showed it would restart from S5 (but not with a short time delay of 30 seconds, 5 minutes is OK, so somewhere between the two is its minimum limit).

In the end, the total changes to BIOS (from defaults) were

  • Standard CMOS settings:
    • Date and time set
    • Drive A (floppy) - disabled
  • Advanced CMOS settings:
    • HD priority - the only hard disk
    • Boot order - CDROM, hard disk, disabled
  • Integrated peripherals:
    • Onchip SATA type - AHCI
    • Onchip SATA port 4/5 type - IDE
  • Power management setup:
    • HPET support - disabled
    • Power on by alarm - disabled

Other changes:

  1. mythtv-setwaketime is unchanged
  2. mythtv-suspend is used with parameter 1 as spd, not str
  3. /etc/init.d/mythtv-wakeup is not needed at all, and neither are the links from /etc/rc0.d and /etc/rc6.d
  4. No changes are necessary to /etc/init/hwclock-save.conf

Regarding point 1, one thing I noticed on this and the ASRock motherboard was that every 10-20 reboots it would come up with a message 'attempt to read or write outside HD0' (or words to that effect) and then stop at a grub-rescue command prompt. Not much use for operating unattended.

On looking that up, some people said it was caused by boot files being more than 128GB from the start of the drive. It wasn't that in this case because the boot partition is only 40GB and it is at the front of the disk. Others said you needed to configure the SATA ports as AHCI. Not an option with the ASRock motherboard - didn't have that option (though it can be configured as RAID which apparently might have the same effect).

However the Gigabyte motherboard does allow this, and once I configured the ports in that way, the problem disappeared. But, if all SATA ports are set that way it always boots from the hard drive, never the DVD. Therefore SATA4-5 were left as IDE, and the DVD drive is connected there.

The ASRock motherboard is the basis of another Ubuntu (14.04.4) system now, and that is not coming up with the problem any longer either, so possibly it was a Ubuntu bug, now fixed.

Note that this motherboard with its onboard ATI Radeon HD3200 graphics caused a lot of grief regarding overscan, and in the end I fitted the nVidia video card to it instead and disabled the onboard graphics.

Shutdown check

The other script mentioned at the top was the shutdown check script. This is run by the backend when it wants to shutdown and needs to check if there is any reason why it shouldn't. It will not even attempt to shutdown if it is active, say - recording.

My version of this is /usr/local/bin/mythtv-shutdowncheck, and listed below:

#!/bin/bash # $1 (optional) where to direct output (LOGFILE if not set, or set to '') LOGFILE="/var/log/mythtv/hwclock-WakeTime.log" CMDOUTPUT="/dev/null" MYTHWEBACCESSFILE="/home/pvr/.mythtv/mythtv-mythwebaccesstime" MYTHWEBTIMEOUT="1200" # 20 minutes if [ ! -z "$1" ] ; then LOGFILE=$1 CMDOUTPUT=$1 fi CURTIME=`date +%s` echo "" >> $LOGFILE echo "`date`" " : Shutdown check" >> $LOGFILE ALLOW=1 ps -A | grep -i kodi > $CMDOUTPUT if [ "$?" == 1 ] ; then echo "*** Kodi not running" >> $LOGFILE ALLOW=0 else echo "Kodi running" >> $LOGFILE fi SSH=`ps -A | grep -i ssh -c` ps -A | grep -i ssh > $CMDOUTPUT if [ "$SSH" -gt 2 ] ; then echo "*** SSH session in progress" >> $LOGFILE ALLOW=0 else echo "No SSH session" >> $LOGFILE fi smbstatus -b | grep ipv4: | grep -v nobody > $CMDOUTPUT if [ "$?" == 0 ] ; then echo "*** SAMBA connection(s) active" >> $LOGFILE ALLOW=0 else echo "No SAMBA connection(s)" >> $LOGFILE fi let "SHUTDOWNTIME = `cat $MYTHWEBACCESSFILE` + $MYTHWEBTIMEOUT" if [ $CURTIME -le $SHUTDOWNTIME ] ; then echo "*** Recent MythWeb access" >> $LOGFILE ALLOW=0 else echo "No recent MythWeb access" >> $LOGFILE fi NETSTAT=`netstat -ant | grep -i :80 | grep -i established | awk '($2 != 0 || $3 != 0)'` echo $NETSTAT > $CMDOUTPUT if [ `echo $NETSTAT | awk /./ | wc -l` != 0 ] ; then echo "*** Web transfer in progress" >> $LOGFILE ALLOW=0 else echo "No web transfer in progress" >> $LOGFILE fi lsof -Pni | grep -i vnc | grep 5900 | grep ESTABLISHED > $CMDOUTPUT if [ "$?" == 0 ] ; then echo "*** VNC session in progress" >> $LOGFILE ALLOW=0 else echo "No VNC session in progress" >> $LOGFILE fi echo "mythshutdown script: `mythshutdown --check 1`" >> $LOGFILE if [ "$?" == 1 ] ; then mythshutdown --status 1 echo "*** Mythshutdown script inhibited (status $?)" >> $LOGFILE ALLOW=0 else echo "Mythshutdown script permitted" >> $LOGFILE fi if [ "$ALLOW" == 1 ] ; then echo "Shutdown allowed" >> $LOGFILE exit 0 fi echo "Shutdown denied" >> $LOGFILE exit 1

It runs a series of tests, exiting with code 1 to fail a shutdown, or code 0 if OK to shutdown. The tests are:

  • Is Kodi running? Kodi is started up automatically on boot as it is the frontend I use. It follows therefore that if Kodi is not running it is because I have shut it down for some system maintenance reason. In that case I do not want the backend shutting the system down. When Kodi is active it has it's own mechanisms for inhibiting shutdown.
  • Is there an SSH session in progress? If so, do not shutdown.
  • Is there a SAMBA connection live? If so, do not shut down.
  • Is MythWeb being used (or has it been in the last 20 minutes)? If so, do not shutdown.
  • Is there a web network transfer in progress, eg a large MPEG file copy? If so, do not shutdown.
  • Is there a VNC session in progress? If so, do not shutdown.
  • MythTV also has a shutdown test utility. If that says do not shutdown, then don't.

These tests are specific to my system arrangements.

The only one of these which requires any extra work is MythWeb. Web access is generally connectionless, so you cannot tell if it is active or not. Therefore what was done was to create a script /usr/local/bin/mythtv-logmythwebaccess that writes a time to a file (/home/pvr/.mythtv/mythtv-mythwebaccesstime) on any Mythweb webpage access. This is then checked against the current time in the shutdown check.

In order to do this the file /var/www/html/mythweb/includes/init.php was edited to add a new line exec("mythtv-logmythwebaccess"); after the ob_start(); line, and the user account www-data (the account used by apache) was added to the pvr group to give it write access to the file by running sudo usermod -a -G pvr www-data.

These scripts were all then set up in the backend setup to be run as necessary.

Next: MythTV backend setup


(c) Nightshade Arts 2016
nick@mistoffolees.me.uk