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 wakeupFor 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:
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 capabilitiesThe 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 insteadAnother 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 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 shutdownFinally, 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:
Using the Gigabyte GA-MA78GM-S2H motherboardAll 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:
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
Other changes:
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 checkThe 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:
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 |