#! /opt/QtPalmtop/bin/ruby =begin = cron_inet.rb by Saki Sakira version 1.0 from 2003 January 24 last revised at 2003 February 24 == versions 0.1 03Jan24 first release 0.2 03Jan30 experiental (not released) 0.3 03Jan30 introduce auto waking-up 0.4 03Jan31-Feb02 bug fix, tune 0.5 03Feb04 bug fix, add 'wait_thread', 'p_debug' 0.6 03Feb04 introduce '--config' option 0.7 03Feb05 improve maters around 'sleep' and 'Thread' 0.7a 03Feb06 change $netconnect 0.8 03Feb07-Feb09 include 'netdisconnect()' refine code, change formatn of config file 0.9 03Feb09-Feb10 introduce $myat (bug fix around 'at') introduce '$biff' 1.0 03Feb15-Feb24 introduce INetConnect::card_exist?() improve power-checking remove 'sudo', $myat introcducee cron_dir change the format of config. file =end Version = "1.0" require "crontab" require "getoptlong" require "open3" #### # commands $netconnect = ' "QPE/Network" "connectRequest()"' $suspend = ' "QPE/Application/suspend" "raise()"' $ifconfig = '/sbin/ifconfig ' $touch_cmd = '/usr/bin/touch' $biff = 'echo -n ""' $card_ident = '/sbin/cardctl ident 0' $apm = '/usr/local/bin/apm' $su = '/bin/su' #### # user-definable variables connect_wait_times = 60 cmds_timeout = 300 cron_connect = '' cron_disconnect = '' cron_suspend = '' cron_startup = '' $power_threshold = 50 #### # internal variables terminate = false next_wake_sec = 0 awake = true #conf_fn = ENV['HOME'] + '/.cron_inetrc' conf_fn = '/home/zaurus/.cron_inetrc' host_to_check = '127.0.0.1' check_interval = 5 awake_period = 5 sleepy_period = 8 $debug = false AtDir = '/var/spool/at/' AtTrigger = '/var/spool/at/trigger' #### # files work_dir = '/tmp/cron_inet/' log_lines = 100 log_file = work_dir + 'log' pid_file = work_dir + 'pid' #### # initialize dirs and files if (!FileTest.exist?(work_dir)) then Dir.mkdir(work_dir, "755".oct) end if (FileTest.exist?(pid_file)) then pid = IO.readlines(pid_file)[0].chomp if (/\n\s*#{pid}[^\n]+\/ruby [^\s\n]*#{$0}/ =~ `ps -U 0 w`) then message = $0 + " is already running. \n" $stderr.print message open(log_file, "a") do |f| f.print message end exit end File.delete(pid_file) end open(pid_file, "w") do |f| f.print $$.to_s + "\n" end if (FileTest.exist?(log_file)) then File.chown(0, nil, log_file) File.chmod("644".oct, log_file) end #### # log writer class LogWriter def initialize(file, ln) @file = file @ln = ln if (FileTest.exist?(@file)) then @body = IO.readlines(@file) else @body = Array.new end end def add(str) @body += str.split("\n") open(@file, "a") do |f| f.print str end p_debug(str) str end def truncate ln = @body.size start_i = ln - @ln start_i = 0 if start_i < 0 start_i.times do |i| @body[i] = nil end @body.compact! open(@file, "w") do |f| @body.each do |l| f.print l.chomp, "\n" end end end end log_writer = LogWriter.new(log_file, log_lines) #### # qcop class QcopHandler @qws_display = '' @logname = '' def initialize @logname = `/usr/bin/whoami`.chomp @qws_display = ENV['QWS_DISPLAY'].to_s if (@qws_display == '') then @qws_display = 'Transformed:Rot270:Vga:0' end ENV['LOGNAME'] = @logname end def call(str) cmd = '/opt/QtPalmtop/bin/qcop ' + str qcop_result = '' Open3.popen3(cmd) do |fin, fout, ferr| qcop_result = ferr.read end if (/server disp spec (\S+)/ =~ qcop_result) then @qws_display = $1 ENV['QWS_DISPLAY'] = @qws_display system(cmd) end "\n" end end Qcop = QcopHandler.new #### # sleep watching thread Thread.abort_on_exception = true Thread.new do awake_sec = 0 last_sec = now_sec = Time.now.to_i while (!terminate) do now_sec = Time.now.to_i if (awake) then if (now_sec - last_sec > check_interval * 2) then awake = false awake_sec = now_sec end Thread.critical = false sleep(check_interval) Thread.critical = true else Thread.critical = false if (now_sec - awake_sec >= awake_period) then awake = true end sleep(1) end last_sec = now_sec end end #### # connection-checker to network class INetConnect def initialize(host, times) @host_to_check = host @check_times = times end def card_exist? if (/function: [26] / =~ `#{$card_ident}`) then true else false end end def alive? if (/^0\.0\.0\.0\s+([^\s]+)\s/ =~ `route -n| grep "^0.0.0.0"`) then if (@host_to_check == nil) then ip = $1 else ip = @host_to_check end ping_str = `ping -c 3 #{ip}` if (/time=/ =~ ping_str) then return true end end false end def wait @check_times.times do if (alive?) then return true end sleep(1) end false end def run(start, timeout, log_writer, command) if (!enough_power? or !card_exist?) then log_writer.add(Time.now.to_s + " not enough power or card absent.\n") return end inet_alive = alive? if (!inet_alive) then log_writer.add(Time.now.to_s + " " + "netconnect: " + Qcop.call($netconnect)) end cmd_thread = Thread.new do log_writer.add("wait for connecting: \n") if (wait) then command.call end system($biff) end while (cmd_thread.alive? and (Time.now.to_i - start < timeout)) do sleep(1) end cmd_thread.kill if (cmd_thread.alive?) if (!inet_alive) then log_writer.add(Time.now.to_s + " " + "netdisconnect: \n") netdisconnect end end end inet_connect = INetConnect.new(host_to_check, connect_wait_times) #### # subroutines def get_next_sec(cron) return false if !cron now_sec = Time.now.to_i (1 .. 60*24).each do |min| next_sec = now_sec + min * 60 t = Time.at(next_sec) if (cron.grep(t) != []) then return next_sec end end 0 end def enough_power? apm_r = `#{$apm}` if (/ (\d+)%/ =~ apm_r) then power = $1.to_i return true if (power >= $power_threshold) end if (/AC on-line. / =~ apm_r) then true else false end end def push_next_trigger(tab, logw) return 0 if (!enough_power?) next_sec = get_next_sec(tab) return 0 if !next_sec next_wake_sec = next_sec - (next_sec % 60) file = AtDir + next_wake_sec.to_s + "." + $$.to_s tmp_file = file + ".new" open(tmp_file, "w") do |f| f.print "#!/bin/sh\n" f.print "rm -f $0\n" end File.chmod("755".oct, tmp_file) File.rename(tmp_file, file) open(AtTrigger, "w") do |t| t.print "\n" end logw.add(Time.now.to_s + ": at " + file + "\n") next_wake_sec end def just_waked?(now_sec, wake_sec, period) if (wake_sec <= now_sec && now_sec - wake_sec < period) then true else false end end def netdisconnect `#{$ifconfig}`.split("\n").each do |l| ["eth0", "ppp0"].each do |net| if (/^#{net}\s/ =~ l) then system($ifconfig + " " + net + " down") end end end end def p_debug(str) print str if $debug str end def parse_crontab(str, log_writer) crontab = nil str.split("\n").each do |l| l.chomp! if (/(^\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(\S+)\s+(.*)/ =~ l) then cron = $1 user = $2 cmd = "'" + $3 + "'" crontab = Crontab.new if (crontab == nil) crontab.add(cron) do now_str = Time.now.to_s cmd_str = `#{$su} #{user} -c #{cmd}` if (cmd != '') then log_writer.add(now_str + ": (#{user}) #{cmd}\n") log_writer.add(cmd_str) end end else log_writer.add("format illegal: " + l + "\n") end end crontab end #### # parse options opt = GetoptLong.new opt.set_options( ['--debug', '-d', GetoptLong::NO_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT], ['--config', '-c', GetoptLong::REQUIRED_ARGUMENT]) until opt.terminated? do optname, optarg = opt.get if (optname == '--debug') then $debug = true elsif (optname == '--config') then conf_fn = optarg elsif (optname == '--help') then print <<"HELP_END" #{__FILE__}: version #{Version} by Saki Sakira usage: #{__FILE__} [options] option: --debug, -d output debug messages to stdout --help, -h display this message --config, -c specify configuration file HELP_END exit end end #### # parse config file if (FileTest.exist?(conf_fn)) then open(conf_fn) do |f| eval(f.read) end end ENV['TZ'] = 'JST-9' #### # add crontabs connect_tab = parse_crontab(cron_connect, log_writer) disconnect_tab = parse_crontab(cron_disconnect, log_writer) suspend_tab = parse_crontab(cron_suspend, log_writer) startup_tab = parse_crontab(cron_startup, log_writer) #### # exec startup cmd inet_alive = inet_connect.alive? if (startup_tab.to_s != '') then if (inet_alive) then startup_tab.run else inet_connect.run(Time.now.to_i, cmds_timeout, log_writer, Proc.new{startup_tab.run}) end end startup_tab = nil #### # main next_wake_sec = push_next_trigger(suspend_tab, log_writer) while (!terminate) do now_sec = Time.now.to_i Thread.pass if ($debug) then print "next_wake_sec: ", next_wake_sec, " ", Time.at(next_wake_sec).to_s, "\n" print " now_sec: ", now_sec, " ", Time.at(now_sec).to_s, "\n" print " awake: ", awake.to_s, "\n" end if (!awake and suspend_tab and just_waked?(now_sec, next_wake_sec, sleepy_period)) then p_debug "sleep : \n" awake = true next_wake_sec = 0 system('echo "' + __FILE__ + ' : waking..." | /usr/bin/wall') log_writer.add(Time.at(now_sec).to_s + " : just_waked\n") inet_connect.run(now_sec, cmds_timeout, log_writer, Proc.new{suspend_tab.run}) log_writer.add("done.\n") wait_thread = Thread.new {sleep(50)} Thread.pass next_wake_sec = push_next_trigger(suspend_tab, log_writer) log_writer.truncate sleep(0.5) Qcop.call($suspend) wait_thread.join else print "awake : \n" if $debug if (inet_connect.alive?) then if (connect_tab) then connect_tab.run end else if (disconnect_tab and disconnect_tab.grep(Time.at(now_sec)) != []) then inet_connect.run(now_sec, cmds_timeout, log_writer, Proc.new{disconnect_tab.run}) end end if (next_wake_sec <= Time.now.to_i) then next_wake_sec = push_next_trigger(suspend_tab, log_writer) end log_writer.truncate system($biff) time_next = next_wake_sec - Time.now.to_i if (time_next > 0 and time_next < 60) then sleep(time_next - 0.5) else sleep_sec = 60 - (Time.now.to_i % 60) if (sleep_sec < 40) then sleep(sleep_sec + 0.5) else sleep(59) end end end end