Video Disk Recorder

Die folgenden Scripts ermöglichen den programmierten Start des VDR-Rechners aus dem Standby mittels ACPI auch dann, wenn der nächste Timer Event mehr als 24 Stunden in der Zukunft liegt. In solchen Fällen wird der Rechner um 0.00 Uhr Lokalzeit gestartet und sofort wieder heruntergefahren.

Voraussetzung:
- ACPI-fähiges BIOS eingestellt auf tägliches Wakeup und UTC/GMT
- Kernel mit ACPI-Unterstützung (/proc/acpi/alarm muß vorhanden sein)

Anregungen:
http://www.hubertus-sandmann.homepage.t-online.de/vdr_wakeup.htm
http://linvdr.org/

Nutzungslizenz:
GNU GPL


#!/bin/sh
#
# /etc/init.d/vdr
#
# starts and stops the video disk recorder.
#
# vdr specific parts:
# Copyright (C) Wolfram Pienkoss At Holzland Thur de
#
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/src/vdr/STUFF/vdr.sh
NAME=vdr
DESC="video disk recorder"

test -x $DAEMON || exit 0

set -e

case "$1" in
    start)
        echo -n "Starting $DESC: $NAME"
        $DAEMON start "power on"
        echo "."
        ;;
    stop)
        echo -n "Stopping $DESC: $NAME"
        $DAEMON stop "power off"
        echo "."
        ;;
    restart|force-reload)
        echo -n "Restarting $DESC: $NAME"
        $DAEMON restart
        echo "."
        ;;
    *)
        N=/etc/init.d/$NAME
        echo "Usage: $N {start|stop|restart|force-reload}" >&2
        exit 1
        ;;
esac

exit 0





#!/bin/bash
#
# /usr/local/src/vdr/STUFF/vdr.sh
#
# start stop script for the video disk recorder
#
# copyright (C) 2004 Wolfram Pienkoss At Holzland Thur de
#
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11
export VDR_LOAD_PID_FILE=/var/run/run-vdr.pid
export VDR_START_STOP_CMD=$0

modules_unload () {
    for mod in dvb-ttpci bttv ivtv snd-via82xx evdev tvaudio \
            ves1820 saa7115 msp3400 tuner; do
        rmmod -r $mod
    done
}

modules_load () {
    modules_unload &>/dev/null
    for mod in evdev snd-via82xx ivtv bttv dvb-ttpci; do
        modprobe $mod
    done
}

eth_config () {
    ethtool -s eth0 wol g
}

sounddev_config () {
    amixer sset Aux,0 96%,96% unmute &>/dev/null
    amixer sset Master,0 96%,96% unmute &>/dev/null
}

lircd_unload () {
    local PID_FILE=/var/run/lircd.pid

    test -e $PID_FILE && kill $(< $PID_FILE)
}

lircd_load () {
    lircd_unload
    lircd
}

nxtvepg_unload () {
    su vdr -c "/usr/local/bin/nxtvepg -card 1 -daemonstop \
        -rcfile /video/plugins/nxtvepg.conf"
}

nxtvepg_load () {
    nxtvepg_unload
    su vdr -c "/usr/local/bin/nxtvepg -card 1 -daemon \
        -rcfile /video/plugins/nxtvepg.conf"
}

vdr_unload () {
    test -e $VDR_LOAD_PID_FILE && {
        local VDR_RUN_PID=$(< $VDR_LOAD_PID_FILE)
        test -n "$VDR_RUN_PID" && {
            local VDR_PID=$(pstree -p $VDR_RUN_PID|grep $VDR_RUN_PID|\
                        (IFS=\(\); read A B C D E; echo $D))
            kill -TERM $VDR_RUN_PID
            test -n "$VDR_PID" && kill -TERM $VDR_PID &>/dev/null
            sleep 2
        }
    }
}

vdr_run () {
    local -x LD_ASSUME_KERNEL=2.4.1
    local -x LANG=de_DE
    local -x LC_COLLATE=de_DE
    local VDR_LOAD_DIR=/usr/local/src/vdr/VDR
    local VDR_LOAD_CMD=./vdr
    local PLUGIN_CONF=/video/plugins/plugins.conf
    local POWERCTRL=/usr/local/src/vdr/STUFF/vdr-powerctrl.pl
    local NOADEXEC=/usr/local/src/vdr/STUFF/noad.sh
    local IFS=$' \t\n'
    local CUR_DIR=$(pwd)
    local PLUGINS=$(grep -Ev "^#" $PLUGIN_CONF | \
        sed -e "s/^/-P \"/" -e s/$/\"/)

    exit_proc () {
        cd $CUR_DIR
        rm -f $VDR_LOAD_PID_FILE
        exit 0
    }


    trap exit_proc SIGTERM
    echo $$ > $VDR_LOAD_PID_FILE
    cd $VDR_LOAD_DIR
    eval $VDR_LOAD_CMD $PLUGINS -r $NOADEXEC -s $POWERCTRL &>/dev/null
    if (($? != 1)); then
        exit_proc
    fi
    cd $CUR_DIR
    sleep 1
    $VDR_START_STOP_CMD restart &
    while :; do
        sleep 1
    done
}

vdr_load () {
    vdr_unload
    /bin/bash -c vdr_run < /dev/null &>/dev/null &
}

vdradmin_unload () {
    local CUR_DIR=$(pwd)
    cd ../VDRADMIN
    ./vdradmind.pl --kill
    cd $CUR_DIR
}

vdradmin_load () {
    local CUR_DIR=$(pwd)
    vdradmin_unload
    cd ../VDRADMIN
    ./vdradmind.pl &>/dev/null
    cd $CUR_DIR
}


export -f vdr_run
POWER_PARAM="restart now"
test -n "$2" && POWER_PARAM="$2"
cd $(dirname $0)

case "$1" in
    start)
        ./vdr-powerctrl.pl "$POWER_PARAM" && exit
        modules_load
        # eth_config
        sounddev_config
        # lircd_load
        nxtvepg_load
        vdr_load
        sleep 10
        vdradmin_load
        ;;
    stop)
        vdradmin_unload
        vdr_unload
        nxtvepg_unload
        lircd_unload
        sleep 2
        modules_unload
        ./vdr-powerctrl.pl "$POWER_PARAM"
        ;;
    restart)
        $0 stop "$POWER_PARAM"
        $0 start "$POWER_PARAM"
        ;;
    *)
        echo "usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac

exit 0





#!/usr/bin/perl
#
# /usr/local/src/vdr/STUFF/vdr-powerctrl.pl
#
# power off script for the video disk recorder
#
# copyright (C) 2004 Wolfram Pienkoss At Holzland Thur de
#
use strict;
use POSIX qw(strftime sprintf);
use Time::Local;
use File::Basename;
use Socket;
use IO::Handle;

my(%setup, %config, @args, $wakeup_file, $conf_file, $setup_file);
my($next_text, $id_next, $id_delta, $id_channel, $id_title);
my($id_reason, $next, $now, $delta_ok, $lockdir, $lockfile_prefix);
my($lockfile_suffix, $osd_msg_cmd, $id_boot_min, $id_shutdown_min);
my($id_inactivity_timer, $id_channel_timer, $id_inactivity_user);
my($id_channel_user, $id_cur_channel, $id_min_event_timeout);
my($id_min_user_inactivity, $id_setup);

$osd_msg_cmd = "/usr/local/src/vdr/STUFF/osd-msg.sh";
$lockdir = "/tmp";
$lockfile_prefix = "vdr-poweroff";
$lockfile_suffix = "lck";
$wakeup_file = "/proc/acpi/alarm";
$setup_file = "/video/setup.conf";
$conf_file = "/video/plugins/powerctrl.conf";
$next_text = "#\n" .
                "# configuration data for power up and down control\n" .
                "#\n" .
                "# This file is automatically generated - do not edit\n" .
                "# Written at: WRITE_TIME\n" .
                "#\n";
$id_boot_min = "PwrCtrlBoot";
$id_setup = "PwrCtrlSetup";
$id_shutdown_min = "PwrCtrlShutdown";
$id_channel = "TimerChannel";
$id_delta = "TimerDelta";
$id_next = "TimerNext";
$id_reason = "TimerReason";
$id_title = "TimerTitle";
$id_channel_timer = "VdrChannelTimer";
$id_channel_user = "VdrChannelUser";
$id_inactivity_timer = "VdrInactivityTimer";
$id_inactivity_user = "VdrInactivityUser";
$id_cur_channel = "CurrentChannel";
$id_min_event_timeout = "MinEventTimeout";
$id_min_user_inactivity = "MinUserInactivity";


sub question($$) {
    my($answer);

    print(STDOUT $_[0], " [", $_[1], "]: ");
    $answer = <STDIN>;
    if ($answer eq "\n") {
        return($_[1]);
    } else {
        return($answer);
    }
}

sub timelocalday($) {
    my(@time);

    @time = localtime($_[0]);
    $time[0] = 0;
    $time[1] = 0;
    $time[2] = 0;
    return(timelocal(@time));
}

sub set_wakeup_time($) {
    open(FHDL, ">" . $wakeup_file);
    print(FHDL strftime("%Y-%m-%d %H:%M:%S", gmtime($_[0])));
    close(FHDL);
}

sub power_off() {
    system("telinit 0");
}

sub reboot() {
    system("telinit 6");
}

sub send_osd_msg($) {
    my($msg_host, $msg_service, $msg_protoname, $msg_proto, $msg_port);
    my($msg_addr, $msg_peer, $dummy);

    $msg_host = "localhost";
    $msg_service = "svdrp";
    $msg_protoname = "tcp";

    $msg_proto = getprotobyname($msg_protoname);
    $msg_port = getservbyname($msg_service, $msg_protoname);
    $msg_addr = inet_aton($msg_host);
    $msg_peer = sockaddr_in($msg_port, $msg_addr);

    if (socket(SVDRP, PF_INET, SOCK_STREAM, $msg_proto) &&
            connect(SVDRP, $msg_peer)) {
        SVDRP->autoflush();
        print(SVDRP "MESG " . $_[0] . "\n");
        $dummy = <SVDRP>;
        print(SVDRP "QUIT\n");
        $dummy = <SVDRP>;
        close(SVDRP);
    }
}

sub read_config_file($%) {
    my(%options, @options, $file);

    $file = shift;
    %options = @_;

    open(FHDL, "<" . $file);
    @options = grep(!/^#/, <FHDL>);
    close(FHDL);

    chomp(@options);

    foreach my $element (@options) {
        my($key, $value);

        $element =~ s/=/\n/;
        ($key, $value) = split(/\n/, $element);
        $key =~ s/(^ *)|( *$)//g;
        $value =~ s/(^ *)|( *$)//g;
        $options{$key} = $value;
    }
    return(%options);
}

sub read_setup() {
    my(%void);

    return(read_config_file($setup_file, %void));
}

sub conf_defaults() {
    my(%defaults, %setup);

    %setup = read_setup();
    $defaults{$id_boot_min} = 5;
    $defaults{$id_shutdown_min} = 5;
    $defaults{$id_inactivity_timer} = 1;
    $defaults{$id_channel_timer} = 1;
    $defaults{$id_setup} = 0;
    $defaults{$id_inactivity_user} = $setup{$id_min_user_inactivity};
    $defaults{$id_channel_user} = $setup{$id_cur_channel};
    return(%defaults);
}

sub read_conf() {
    my(%defaults);

    %defaults = conf_defaults();
    return(read_config_file($conf_file, %defaults));
}

sub write_config_file($$%) {
    my($file, $text, %options);

    $file = shift;
    $text = shift;
    %options = @_;

    if (open(FHDL, ">" . $file)) {
        if (length($text)) {
            print(FHDL $text);
        }
        foreach my $key (sort(keys(%options))) {
            print(FHDL $key, " = ", $options{$key}, "\n");
        }
        close(FHDL);
    }
}

sub write_conf(%) {
    my($timestr, $text);

    $timestr = strftime("%a %b %d %T %Z %G", localtime(time));
    $text = $next_text;
    $text =~ s/WRITE_TIME/$timestr/g;
    write_config_file($conf_file, $text, @_);
}

sub write_setup(%) {
    my($void);

    write_config_file($setup_file, $void, @_);
}


@args = @ARGV;

if (scalar(@args) == 1) {
    if ($args[0] eq "restart now") {
        # restarting the vdr
        exit(2);
    }

    if ($args[0] eq "-s") {
        # setup
        my($autosetup);

        %config = read_conf();
        $config{$id_boot_min} = question("Bootzeit in Minuten", $config{$id_boot_min});
        $config{$id_shutdown_min} = question("Shutdownzeit in Minuten", $config{$id_shutdown_min});
        $config{$id_inactivity_timer} = question("Abschaltinaktivität in Minuten bei Start durch Timer", $config{$id_inactivity_timer});
        $config{$id_channel_timer} = question("Kanalnummer bei Start durch Timer", $config{$id_channel_timer});
        write_conf(%config);
        %setup = read_setup();
        $setup{$id_min_event_timeout} = $config{$id_boot_min} + $config{$id_shutdown_min};
        write_setup(%setup);
        $autosetup = question("Boot- und Shutdownzeit automatisch ermitteln?", "Ja");
        if ($autosetup eq "Ja") {
            print(STDOUT "Der Rechner wird jetzt zweimal automatisch neu gestartet\n");
            $config{$id_setup} = 1;
            $config{$id_shutdown_min} = time();
            write_conf(%config);
            reboot();
        }
        exit(1);
    }

    if ($args[0] eq "power off") {
        # power off
        %setup = read_setup();
        %config = read_conf();
        $config{$id_channel_user} = $setup{$id_cur_channel};
        write_conf(%config);
        exit(2);
    }

    if ($args[0] eq "power on") {
        # power on
        $now = time();
        %config = read_conf();

        if ($config{$id_setup} != 0) {
            # autosetup active
            if ($config{$id_setup} == 1) {
                # time to reboot
                $config{$id_setup} = 2;
                $config{$id_shutdown_min} = time() - $config{$id_shutdown_min};
                $config{$id_boot_min} = time() + $config{$id_shutdown_min};
                write_conf(%config);
                set_wakeup_time($config{$id_boot_min});
                power_off();
                exit(0);
            }
            if ($config{$id_setup} == 2) {
                # time to boot; ready
                $config{$id_setup} = 0;
                $config{$id_boot_min} = time() - $config{$id_boot_min};
                $config{$id_shutdown_min} = $config{$id_shutdown_min} - $config{$id_boot_min};
                $config{$id_boot_min} = int(($config{$id_boot_min} + 30 + 59) / 60);
                $config{$id_shutdown_min} = int(($config{$id_shutdown_min} + 30 + 59) / 60);
                write_conf(%config);
            }
        }

        $args[0] = $config{$id_next};
        $args[1] = $config{$id_delta};
        $args[2] = $config{$id_channel};
        $args[3] = $config{$id_title};
        $args[4] = 0;

        %setup = read_setup();
        if (($args[0] - $config{$id_boot_min} * 60 <= $now) && ($now <= $args[0])) {
            # start by timer assumed
            $setup{$id_cur_channel} = $config{$id_channel_timer};
            $setup{$id_min_user_inactivity} = $config{$id_inactivity_timer};
        } else {
            # manually start assumed
            $setup{$id_cur_channel} = $config{$id_channel_user};
            $setup{$id_min_user_inactivity} = $config{$id_inactivity_user};
        }
        $setup{$id_min_event_timeout} = $config{$id_boot_min} + $config{$id_shutdown_min};
        write_setup(%setup);

        if ($config{$id_boot_min} * 60 < $now - timelocalday($now)) {
            # power on manually or by regular BIOS timer event and
            # ready to start vdr
            exit(2);
        }
    }
}

if (scalar(@args) != 5) {
    # not a power off call
    die("wrong parameter number\n\nusage: $0 -s\n\n");
}

if (opendir(DHDL, $lockdir)) {
    my(@locks);

    @locks = grep(/^$lockfile_prefix\.(.*)\.$lockfile_suffix$/, readdir(DHDL));
    close(DHDL);

    foreach my $lock (@locks) {
        my($file);

        $file = $lockdir . "/" . $lock;

        if (open(FHDL, "<" . $file)) {
            my($pid);

            $pid = <FHDL>;
            close(FHDL);

            if (($pid eq "manual") || kill(0, $pid)) {
                # power off locked
                #
                # produces a deadlock:
                # send_osd_msg("Abschalten vorübergehend gesperrt.");
                system($osd_msg_cmd . " \"Abschalten vorübergehend gesperrt.\"");
                exit(2);
            } else {
                unlink($file);
            }
        }
    }
}

if (!-e $wakeup_file) {
    die("current kernel doesn't support ACPI wake up\n");
}

%config = read_conf();
$now = time();
$next = $args[0] - $config{$id_boot_min} * 60;
$delta_ok = $next > $now + $config{$id_shutdown_min} * 60;

if (!$delta_ok && $args[4]) {
    $args[0] = 0;
}

if ($delta_ok && ($next < $now + 60 * 60 * 24)) {
    set_wakeup_time($next);
} else {
    set_wakeup_time(timelocalday($now));
}

if ($delta_ok || !$args[0] || $args[4]) {
    $config{$id_next} = $args[0];
    $config{$id_delta} = $args[1];
    $config{$id_channel} = $args[2];
    $config{$id_title} = $args[3];
    $config{$id_reason} = $args[4];
    if ($args[4]) {
        %setup = read_setup();
        $config{$id_inactivity_user} = $setup{$id_min_user_inactivity};
    }
    write_conf(%config);
    power_off();
    exit(0);
}

exit(1);