RTC - hodiny reálného času
Základem fungování automatického probuzení/zapnutí systému jsou hodiny reálného času, které jsou přímo v HW systému. Tyto hodiny jsou napájeny baterií, která je udržuje v chodu, i když je systém vypnutý a odpojený od sítě. Stejná baterie zajišťuje udržení nastavení BIOSu. Probouzet se na budíček umí sytémy podporující APM, či novější ACPI, cca od roku 2000. ACPI standard rozšiřuje možnosti RTC o delší dobu plánování budíku a podporu probuzení z hibernace, tedy z uspání na disk. RTC může, krom alarmu v určitý čas, také generovat přerušení s určitou frekvencí (2 Hz až 8192 Hz, po násobcích 2), nebo generovat přerušení při každé aktualizaci hodin, tedy každou sekundu (1 Hz). Používá se přerušení IRQ 8 a výstup je dostupný přes zařízení /dev/rtc0, /dev/rtc na něj bývá linkováno (i v Ubuntu). Zmíněné tři generátory přerušení jsou nezávislé, samostatně nastavitelné. /dev/rtc je pouze pro čtení a otevřeno může být jen jednou - dokud ho předchozí klient nezavře, ostatní dostanou chybu, že zařízení je používáno.RTC jsou pod Linuxem vždy nastaveny na UTC čas, nejsou posouvány podle lokálního času jako ve Windows. Nabootování do Windows tedy RTC rozhodí všem, kteří nejsou v pásmu UTC. Linux jinak RTC aktualizuje každých 11 minut, přičemž na chvíli vypne periodické přerušení, což může způsobit problém aplikaci, která na něm závisí. I aktualizace se ale dá vypnout.
U některých základních desek najdete v BIOSu v sekci Power Managementu možnost nastavit si alarm pro automatické probuzení systému přímo. Pro použití z operačního systému je v zásadě třeba nastavení budíku v BIOSu zrušit, jen nechat povoleno probouzení systému RTC alarmem.
K nastavení RTC modulu se dostanete přes procfs:
$ cat /proc/driver/rtc
rtc_time : 11:21:55
rtc_date : 2014-02-09
alrm_time : 12:11:58
alrm_date : 2014-02-09
alarm_IRQ : yes
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1024
max user IRQ frequency : 64
24hr : yes
periodic_IRQ : no
update_IRQ : no
HPET_emulated : yes
BCD : yes
DST_enable : no
periodic_freq : 1024
batt_status : okay
rtc_time : 11:21:55
rtc_date : 2014-02-09
alrm_time : 12:11:58
alrm_date : 2014-02-09
alarm_IRQ : yes
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1024
max user IRQ frequency : 64
24hr : yes
periodic_IRQ : no
update_IRQ : no
HPET_emulated : yes
BCD : yes
DST_enable : no
periodic_freq : 1024
batt_status : okay
Je tu aktuální čas a datum, nastavení a stav budíku, i ostatních generátorů přerušní. Na mém příkladu můžete vidět, že je budík aktivní (alarm_IRQ : yes).
Nastavení RTC budíku
Pro nastavení budíku se používá čas v sekundách od začátku roku 1970. Protože jsme si řekli, že je to čas UTC, od kterého jsme u nás jednu až dvě hodiny napřed, podle toho, jestli máme zimní, nebo letní čas, je potřeba čas vždy správně přepočítat. K tomu naštěstí máme chytré nástroje přímo v systému.Nastavení času budíku se dělá zápisem do souboru /sys/class/rtc/rtc0/wakealarm, k němuž má přístup pouze root a i ten tam může zapsat nový čas pouze tehdy, když ještě budík není nastaven. Pokud nastaven je a chcete ho změnit, musíte tam nejprve poslat znak 0 (nula), čímž předchozí nastavení zrušíte.
Pro konverzi času je nejjednodušší použít příkaz date. Můžete si alarm nastavit například na určitou hodinu následujícího dne (jak bylo řečeno - pod rootem a hodit před to jednoduše sudo nestačí, to by se nevztahovalo na použité přesměrování - když už, tak třeba sudo su -c 'příkaz'):
echo 0 > /sys/class/rtc/rtc0/wakealarm
date +%s -d "next day 7:00" > /sys/class/rtc/rtc0/wakealarm
date +%s -d "next day 7:00" > /sys/class/rtc/rtc0/wakealarm
Naopak přečíst a zkonvertovat aktuální nastavení budíku můžete třeba takto:
cat /sys/class/rtc/rtc0/wakealarm | xargs -I {} date +"%d/%m/%Y %H:%M" -d "@{}"
10/02/2014 17:00
10/02/2014 17:00
Pokud dostanete prázdný řetězec, budík nastaven není.
Podívat se můžete i na příkaz rtcwake, který umí systém okamžitě uspat, či vypnout, a současně nastavit čas, kdy se má systém probudit.
Odkazy:
https://www.kernel.org/doc/Documentation/rtc.txt
http://man7.org/linux/man-pages/man4/rtc.4.html
Probuzení systému podle rozvrhu nahrávání Tvheadend
Mám tedy TV server, který mi může nějaký ten pořad nahrát, kliknutím v prohlížeči, nebo XBMC si ho můžu objednat, ale vzhledem k tomu, že mi počítač neběží 24 hodin denně, bude potřeba vyřešit nějakou automatiku, která si systém sama zapne, když je potřeba a případně zase po dokonání díla zpátky uspí. Mé řešení by mělo umět zhruba toto:- při uspávání systému si přečíst plán nahrávání a nastavit RTC alarm na nejbližší pořad
- při probuzení by mělo být schopno určit, zda proběhlo podle nastavené automatiky, nebo jinak
- pokud automaticky, měl by se systém po skončení nahrávání opět uspat
- ovšem pouze tehdy, pokud mezitím uživatel systém nezačal používat, aby mu neusnul pod rukama
Takže jsem se tím začal prokousávat, začal jsem wiki Tvheadend. Máte-li při uspávání, nebo probouzení s některými moduly, nebo aplikacemi problém, můžete se tam podívat, jak je při suspendu odebrat a při probuzení opět zavést. Další skript, který řeší vlastní programování RTC alarmu má podle mého k dokonalosti hodně daleko, ale vlastní zpracování dat z logů funkční je, i když je v tom mrak zbytečností. Tak jsem to trochu přepsal a doplnil.
Teoreticky by se dalo dostat k datům, které drží přímo Tvheadend v xml formátu, jenže to vyžaduje přihlášení a navíc by tu byly trochu větší latence, takže se probereme utrousenými logy, ve kterých ta data najdeme také. Každý timer má vlastní log v adresáři
~hts/.hts/tvheadend/dvr/log/
Pokud by vám připadalo zvláštní to ~hts, shell to přeloží do celé cesty $HOME uživatele hts, ve kterém Tvheadend operuje (pokud jste to neměnili). K hrabání se v těchto datech musíte mít oprávnění, takže můžete použít roota. Suspend/resume skripty běží stejně pod rootem, takže to není potřeba řešit. Nakonec jsem uplácal následující skript, který mám v souboru
/etc/pm/sleep.d/05_tv-timer.sh
#!/bin/bash
#
# set RTC Wakeup alarm
# script does not check if recording is in progress
#
suspend_fn()
{
# pokud systém uspává post-process skript, musí se poklidit
pkill -f waitxinput
rm -f /run/shm/tvsuspend
# časová rezerva pro probuzení systému před začátkem nahrávání
safe_margin=60
# cesta k logům rekordéru
log_path=~hts/.hts/tvheadend/dvr/log
######################
start_date=0
current_date=`date +%s`
for i in $( find "$log_path" -type f ); do
tmp_start=`sed -nr '/"start":/s/[^0-9]*([0-9]+).*/\1/p' $i`
# kontrola prošlých timerů
if [ $tmp_start -gt $current_date ]; then
# bližší čas si zapamatujem
if [ $start_date -eq 0 -o $tmp_start -lt $start_date ]; then
start_date=$tmp_start
fi
fi
done
wake_date=$((start_date-safe_margin))
# nastavení budíčku, pokud byl nalezen vyhovující timer
if [ $start_date -ne 0 ]; then
echo 0 > /sys/class/rtc/rtc0/wakealarm
echo $wake_date > /sys/class/rtc/rtc0/wakealarm
# nastavovaný čas bude uložen pro použití v resume skriptu
echo $wake_date > /run/shm/rtc_alarm_time
# pokud není k budíku důvod, pro jistotu poklidíme
else rm -f /run/shm/rtc_alarm_time
fi
}
resume_fn()
{
# pokud se najde záznam o času budíku, porovná se s aktuálním časem
if [ -e /run/shm/rtc_alarm_time ]; then
rat=`cat /run/shm/rtc_alarm_time`
ct=`date +%s`
d=$((ct-rat))
d=${d/-/} # absolutní hodnota sprostým odebráním případného mínusu
echo $d
if [ $d -gt 60 ]; then
rm -f /run/shm/rtc_alarm_time
# rozdíl mezi časem posledně nastaveného budíku a časem při tomto probuzení
# je větší, než minuta, takže se systém pravděpodobně neprobudil automaticky
else
killall xbmc.bin & # XBMC systém automaticky uspává při nečinnosti
su gdh -c 'touch /run/shm/tvsuspend; export DISPLAY=:0; ~/bin/waitxinput >/dev/null; rm /run/shm/tvsuspend' &
# systém se pravděpodobně probudil automaticky, takže byl vytvořen kontrolní soubor
# a spuštěn pythonní skript "waitxinput", který v případě, že uživatel pohne myší,
# nebo stiskne klávesu klávesnice, skončí, čímž dojde ke smazání kontrolního souboru
# a post-processor skript spuštěný Tvheadend po skončení nahrávání pak systém neuspí
fi
fi
}
case $1 in
suspend )
suspend_fn
;;
resume )
resume_fn
;;
esac
exit 0
#
# set RTC Wakeup alarm
# script does not check if recording is in progress
#
suspend_fn()
{
# pokud systém uspává post-process skript, musí se poklidit
pkill -f waitxinput
rm -f /run/shm/tvsuspend
# časová rezerva pro probuzení systému před začátkem nahrávání
safe_margin=60
# cesta k logům rekordéru
log_path=~hts/.hts/tvheadend/dvr/log
######################
start_date=0
current_date=`date +%s`
for i in $( find "$log_path" -type f ); do
tmp_start=`sed -nr '/"start":/s/[^0-9]*([0-9]+).*/\1/p' $i`
# kontrola prošlých timerů
if [ $tmp_start -gt $current_date ]; then
# bližší čas si zapamatujem
if [ $start_date -eq 0 -o $tmp_start -lt $start_date ]; then
start_date=$tmp_start
fi
fi
done
wake_date=$((start_date-safe_margin))
# nastavení budíčku, pokud byl nalezen vyhovující timer
if [ $start_date -ne 0 ]; then
echo 0 > /sys/class/rtc/rtc0/wakealarm
echo $wake_date > /sys/class/rtc/rtc0/wakealarm
# nastavovaný čas bude uložen pro použití v resume skriptu
echo $wake_date > /run/shm/rtc_alarm_time
# pokud není k budíku důvod, pro jistotu poklidíme
else rm -f /run/shm/rtc_alarm_time
fi
}
resume_fn()
{
# pokud se najde záznam o času budíku, porovná se s aktuálním časem
if [ -e /run/shm/rtc_alarm_time ]; then
rat=`cat /run/shm/rtc_alarm_time`
ct=`date +%s`
d=$((ct-rat))
d=${d/-/} # absolutní hodnota sprostým odebráním případného mínusu
echo $d
if [ $d -gt 60 ]; then
rm -f /run/shm/rtc_alarm_time
# rozdíl mezi časem posledně nastaveného budíku a časem při tomto probuzení
# je větší, než minuta, takže se systém pravděpodobně neprobudil automaticky
else
killall xbmc.bin & # XBMC systém automaticky uspává při nečinnosti
su gdh -c 'touch /run/shm/tvsuspend; export DISPLAY=:0; ~/bin/waitxinput >/dev/null; rm /run/shm/tvsuspend' &
# systém se pravděpodobně probudil automaticky, takže byl vytvořen kontrolní soubor
# a spuštěn pythonní skript "waitxinput", který v případě, že uživatel pohne myší,
# nebo stiskne klávesu klávesnice, skončí, čímž dojde ke smazání kontrolního souboru
# a post-processor skript spuštěný Tvheadend po skončení nahrávání pak systém neuspí
fi
fi
}
case $1 in
suspend )
suspend_fn
;;
resume )
resume_fn
;;
esac
exit 0
waitxinput
Součástí předchozího skriptu je volání skriptu waitxinput, který mám v adresáři ~/bin/. Jak je zmíněno v komentáři, je to jednoduchý trigger, který je aktivován jakýmkoli vstupem z klávesnice, nebo myši. Zkrátka čeká, dokud nějaký vstup nezaznamená a pak skončí, takže se mohou provést příkazy za ním.
Pro jeho zfunkčnění je potřeba doinstalovat modul Xlib:
sudo apt-get install python-xlib
#!/usr/bin/python
# skript čeká na jakýkoliv vstup z klávesnice, či myši, aby se ukončil
from Xlib import X
from Xlib.display import Display
display = Display(':0')
root = display.screen().root
root.grab_pointer(True,
X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime)
root.grab_keyboard(True,
X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
display.next_event()
# skript čeká na jakýkoliv vstup z klávesnice, či myši, aby se ukončil
from Xlib import X
from Xlib.display import Display
display = Display(':0')
root = display.screen().root
root.grab_pointer(True,
X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime)
root.grab_keyboard(True,
X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
display.next_event()
Automatické vypnutí po skončení nahrávání
Tvheadend takovou funkci přímo nenabízí, ale umí po skončení nahrávání spustit příkaz. Nastavení je v záložce Configuration > Recording > Digital Video Recorder položka Post-processor command:, sem si můžete napsat cestu k vlastnímu skriptu, který může jen systém zase uspat, nebo vypnout. Skript si hodíte například do souboru~hts/suspend.sh
Já tam mám následující:
#!/bin/bash
# pokud resume skript vytvořil a nesmazal soubor tvsuspend
# systém je vhodné po skončení nahrávání uspat
if [ -e /run/shm/tvsuspend ]; then
sudo /usr/sbin/pm-suspend
fi
# pokud resume skript vytvořil a nesmazal soubor tvsuspend
# systém je vhodné po skončení nahrávání uspat
if [ -e /run/shm/tvsuspend ]; then
sudo /usr/sbin/pm-suspend
fi
A protože je příkaz pm-suspend pouze pro roota, musí se spouštět se sudo a udělit uživateli hts právo ho spouštět bez hesla editací sudoers. Takže spustíte příkaz:
sudo visudo
a na konec souboru si doplníte:
hts ALL=NOPASSWD: /usr/sbin/pm-suspend # TVHeadend
Žádné komentáře:
Okomentovat
Zkuste prosím při komentováni používat místo volby Anonymní volbu Název/adresa URL, kde vyplníte nějakou přezdívku, adresu zadávat nemusíte. Vědět, které příspěvky jsou od jednoho člověka, je fajn. Díky.
Pokud by se vám náhodou odeslaný komentář na stránce nezobrazil, vytáhnu ho z koše hned jak si toho všimnu. I Google spam filter se občas sekne.