#!/usr/bin/perl use warnings; use strict; use utf8; use DBI; use Time::Local 'timelocal'; my $DEBUG = 1; my $MARIADB_CONNECT = 'DBI:mysql:epgstation:127.0.0.1'; my $MARIADB_USER = 'epgstation'; my $MARIADB_PASSWD = 'epgstation'; my @IMPORTANT_PROGRAMS = ( 'recpt1', 'ffmpeg', 'sshd: rarul', 'smbd: client', ); my $LOGFILENAME = ''; my $LOGFILEFP; sub logfilecheck { my @retval = localtime(time()); # prepare logfilename # getting Sunday-based date my $logtime = timelocal(0, 0, 0, $retval[3] - $retval[6], $retval[4], $retval[5]); # 5 hours delayed filename $logtime -= 5 * 3600; my @logval = localtime($logtime); my $logfilename = sprintf("%02d%02d%02d.log", $logval[5] - 100, 1 + $logval[4], $logval[3]); if ($LOGFILENAME ne $logfilename) { if ($LOGFILENAME ne '') { close $LOGFILEFP; } open $LOGFILEFP, '>>', '/root/suspendd/log/' . $logfilename; select($LOGFILEFP); $|=1; $LOGFILENAME = $logfilename; } # for return value my $timestr = sprintf("%04d/%02d/%02d %02d:%02d:%02d", 1900 + $retval[5], 1 + $retval[4], $retval[3], $retval[2], $retval[1], $retval[0]); return $timestr; } sub debugprint { if ($DEBUG) { my $timestr = logfilecheck(); print $LOGFILEFP $timestr . ' ' . $_[0] . "\n"; } } sub do_suspend { my $suspend_time_sec = shift; my $cmd = 'rtcwake -m mem -s ' . $suspend_time_sec; debugprint("go suspnd $suspend_time_sec"); ## by unknown reason, log file goes closed when suspended. ## so, close here, and open again. #close $LOGFILEFP; #$LOGFILENAME = ''; system($cmd); debugprint('suspend done'); # require to call reset for epgstation sleep 10; system('curl -X POST "http://127.0.0.1:8888/api/recording/resettimer"'); } sub check_next_day_epg { # check new day is coming. # if 5 minutes or less, we stay without suspend. my $now_epoch = time() + 5 * 60; my @retval = localtime($now_epoch); # check if now is in 23:55 - 00:25 if ($retval[2] == 0 && ($retval[1] >=0 && $retval[1] < 25)) { return 0; } # 3 days after, and 00:10:00. We don't need to wake up at strict just time. my $next_day_epoch = timelocal(0, 10, 0, 3 + $retval[3], $retval[4], $retval[5]); return $next_day_epoch - $now_epoch; } sub check_reserve_sched { # check the most recent reserve program my $reserve_count = 0; my $startat = 0; my $dbi = DBI->connect($MARIADB_CONNECT, $MARIADB_USER, $MARIADB_PASSWD); my $sth1 = $dbi->prepare('select count(id) from reserve;'); $sth1->execute; if ($sth1->rows > 0) { $reserve_count = ($sth1->fetchrow_array)[0]; } $sth1->finish; if ($reserve_count > 0) { my $sth2 = $dbi->prepare('select min(startAt) from reserve;'); $sth2->execute; if ($sth2->rows > 0) { $startat = ($sth2->fetchrow_array)[0]; } $sth2->finish; } $dbi->disconnect; if ($startat == 0) { # no reserve found. return 3 * 24 * 3600; } # startAt is in milli seconds. $startat /= 1000; # keep more 10 minutes to prevent suspend. my $remain_second = $startat - time() - 10 * 60; if ($remain_second <= 0) { # reserve event in soon or already started. return 0; } return $remain_second ; } sub check_important_prog_present_inner { # walk in /proc and check programs are running. my $present = 0; opendir my $dh, './' or return 0; my @mydirs = readdir $dh; closedir $dh; foreach my $mydir (@mydirs) { if ($mydir !~ /^[0-9]+$/) { next; } open my $fh, '<', $mydir . '/cmdline' or next; my $read_size = sysread $fh, my $read_cmdline, 256; close $fh; if ($read_size <= 0) { next; } foreach my $my_prog (@IMPORTANT_PROGRAMS) { if (index($read_cmdline, $my_prog) >= 0) { $present++; #print "hit $mydir $read_cmdline \n"; last; } } if ($present > 0) { last; } } if ($present > 0) { return 0; } # allow to sleep in 3 days return 3 * 24 * 3600; } sub check_important_prog_present { my $retval = check_important_prog_present_inner(); if ($retval != 0) { # Even if important program is not present, # it may be just a timing to wait to launch # new programs. So try again after 5 seconds. sleep 5; $retval = check_important_prog_present_inner();; } return $retval; } sub do_main { # daemon my $pid = fork(); if ($pid < 0) { print 'fork fail' . "\n"; return 1 } elsif ($pid > 0) { # parent print "child $pid created\n"; return 0; } # child chdir '/proc'; debugprint('first 10m'); sleep 10 * 60; while (1) { my $suspend_time_sec = 3 * 24 * 3600; if ($suspend_time_sec > 0) { my $tmp = check_next_day_epg(); debugprint("epg $tmp"); if ($tmp < $suspend_time_sec) { $suspend_time_sec = $tmp; } } if ($suspend_time_sec > 0) { my $tmp = check_reserve_sched(); debugprint("rec $tmp"); if ($tmp < $suspend_time_sec) { $suspend_time_sec = $tmp; } } if ($suspend_time_sec > 0) { my $tmp = check_important_prog_present(); debugprint("prog $tmp"); if ($tmp < $suspend_time_sec) { $suspend_time_sec = $tmp; } } if ($suspend_time_sec <= 5 * 60) { # if 5 minutes or less, then simply sleep debugprint('sleep 5m'); sleep 5 * 60; } else { # or we go suspend do_suspend($suspend_time_sec); # after resumed, we stay 10 minutes then go for loop. debugprint('sleep 10m'); sleep 10 * 60; } debugprint('loop end'); } return 0; } sub do_test { chdir '/proc'; print 'check_important_prog_present() ' . check_important_prog_present() . "\n"; print 'check_reserve_sched() ' . check_reserve_sched() . "\n"; print 'check_next_day_epg() ' . check_next_day_epg() . "\n"; debugprint('a'); sleep(1); debugprint('b'); sleep(1); debugprint('c'); sleep(1); debugprint('d'); sleep(1); debugprint('e'); return 0; } sub main { if (@ARGV >= 1) { if ($ARGV[0] eq 'test') { return do_test(); } elsif ($ARGV[0] eq 'run') { return do_main(); } } print './suspendd.pl [test|run]' . "\n"; return 1; } exit main;