#!/usr/bin/perl -w ##=====================================================================## ## Copyright (C) 2004 Stephen A. Zarkos. All rights reserved. ## Obsid@Sentry.net ##=====================================================================## ## File: petd.pl umask 022; use strict; use POSIX; use Audio::DSP; $ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin"; $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&sig_handler; $SIG{CHLD} = 'IGNORE'; ## $SIG{HUP} = ## FIXME: Re-Read config(when implemented). ##---| Global Variables |---## our ($dsp,$FH) = (); our %conf = (); our $version = '0.2'; MAIN: { ##---| Variables |---## my $status = ''; my $random = ''; ##---| Read/Parse Config File |---## $status = &parse_config; ## FIXME: do something with status? ##---| Daemonize |---## my $pid = fork; exit if $pid; die "Could not fork: $!" unless defined($pid); POSIX::setsid() or die "Cannot start a new session: $!"; ##---| Set User and Group ID |---## if (exists($conf{'user'})) { $> = $< = $conf{'user'}; } if (exists($conf{'group'})) { $( = $) = $conf{'group'}; } if (!(-r '/dev/dsp')) { ## Cannot read the /dev/dsp device(cannot record). print "Error: /dev/dsp is not readable(can't record), exiting. \n"; &sig_handler; } while () { $status = &get_input($conf{'timeout'}); if ($status eq '1') { (print "Input Detected!\n") if ($conf{'debug'} == 1); $random = int(rand(10)) + 1; system('/usr/bin/play', '-v', "$conf{volume}", "$conf{sound${random}}"); sleep $conf{'listen_delay'}; } $status = ''; } exit(0); } ## End Main ##-----------------------------------------------------------------------## ##-----------------------------------------------------------------------## ## Sub get_input(), listens for input. Returns 1 if input threshhold ## received, 2 if there is no input within the timeout threshhold, else ## returns 0 on error. sub get_input { local $SIG{ALRM} = sub { die "NO_INPUT"; }; my $timeout = (defined($_[0]) && (($_[0] > 0) && ($_[0] < 20))) ? $_[0] : 15; my $MAGIC = 4294967295; my $seconds = $conf{'seconds'}; my $i = 0; my $out = ''; my ($buf, $chan, $fmt, $rate) = ($conf{'buffer'}, 1, 8, $conf{'rate'}); my $length = ($chan * $fmt * $rate * $seconds) / 8; $dsp = new Audio::DSP(buffer => $buf, channels => $chan, format => $fmt, rate => $rate); $dsp->init() || ((print $dsp->errstr()) && (&sig_handler)); eval { alarm($timeout); while ($i < $length) { $out = bin2dec($dsp->dread($buf) || ((print $dsp->errstr()) && (&sig_handler))); ($i += $buf) if ($out < $MAGIC); (print "Output: $out, Temp_var: $i\n") if ($conf{'debug'} == 1); $out = ''; } alarm(0); }; if ($@) { if ($@ =~ /NO_INPUT/) { $dsp->clear(); $dsp->close(); return 2; ## Times Out. } else { alarm(0); $dsp->clear(); $dsp->close(); return 0; ## Return Error. } } else { $dsp->clear(); $dsp->close(); return 1; ## Return Success. } return 0; } ## End sub get_input(). ##-----------------------------------------------------------------------## ##-----------------------------------------------------------------------## ## sub parse_config() ## Grabs and parses configuration file for petd. sub parse_config { my $config_file = '/etc/petd/petd.conf'; my $default_sound = 'meow.wav'; my %user_config = (); my @config = (); ## Default configuration values. %conf = ('timeout' => 15, 'seconds' => 0.5, 'buffer' => 2048, 'rate' => 16384, 'volume' => 0.4, 'listen_delay' => 1, 'debug' => 0, 'user' => '', 'group' => '', 'default_sound' => "$default_sound", 'sound1' => '', 'sound2' => '', 'sound3' => '', 'sound4' => '', 'sound5' => '', 'sound6' => '', 'sound7' => '', 'sound8' => '', 'sound9' => '', 'sound10' => ''); if (-f "$config_file") { if (open ($FH,"<$config_file")) { flock($FH,1); @config = <$FH>; close($FH); } else { print "Warning: unable to open ${config_file}. Using defaults.\n"; } } my ($key,$val,$tmp) = (); foreach (@config) { chomp($_); $_ =~ s/#.*//; $_ =~ s/\"+//g; $_ =~ s/\'+//g; $_ =~ s/\`+//g; $_ =~ s/\*+//g; $_ =~ s/^[\s\t]+//; $_ =~ s/[\s\t]+$//; next if ("$_" eq ''); $_ = lc($_); ($key,$val) = split(/[\s\t]+/, $_, 2); foreach $tmp (keys %conf) { next if ($key ne $tmp); if ($val ne '') { $user_config{"$key"} = "$val"; } last; } } @config = (); ##---| Check %user_config for valid values, put values into %conf. |---## ## timeout (1..20) $conf{'timeout'} = (exists($user_config{'timeout'}) && (($user_config{'timeout'} >= 1) && ($user_config{'timeout'} <= 20))) ? $user_config{'timeout'} : $conf{'timeout'}; ## seconds (1..5) $conf{'seconds'} = (exists($user_config{'seconds'}) && (($user_config{'seconds'} >= 1) && ($user_config{'seconds'} <= 5))) ? $user_config{'seconds'} : $conf{'seconds'}; $conf{'buffer'} = (exists($user_config{'buffer'}) && ($user_config{'buffer'} =~ /^\d+$/)) ? int $user_config{'buffer'} : $conf{'buffer'}; $conf{'rate'} = (exists($user_config{'rate'}) && ($user_config{'rate'} =~ /^\d+$/)) ? int $user_config{'rate'} : $conf{'rate'}; # volume (0.1..1) $conf{'volume'} = (exists($user_config{'volume'}) && (($user_config{'volume'} >= 0.1) && ($user_config{'volume'} <= 1))) ? $user_config{'volume'} : $conf{'volume'}; # listen_delay (1..10) $conf{'listen_delay'} = (exists($user_config{'listen_delay'}) && (($user_config{'listen_delay'} >= 1) && ($user_config{'listen_delay'} <= 10))) ? $user_config{'listen_delay'} : $conf{'listen_delay'}; ## Check $user_config{'user'} and $user_config{'group'} $conf{'user'} = $user_config{'user'} if (exists($user_config{'user'})); $conf{'group'} = $user_config{'group'} if (exists($user_config{'group'})); if ($conf{'user'} ne '') { if (defined(getpwnam($conf{'user'}))) { $conf{'user'} = getpwnam($conf{'user'}); } else { print "Error: No such user: $conf{user}. Exiting.\n"; } } else { delete($conf{'user'}); } if ($conf{'group'} ne '') { if (defined(getgrnam($conf{'group'}))) { $conf{'group'} = getgrnam($conf{'group'}); } else { print "Error: No such group: $conf{group}. Exiting.\n"; } } else { delete($conf{'group'}); } ## Finally, check sounds(1..10) variable. if (exists($user_config{'default_sound'})) { if (-f "$user_config{'default_sound'}") { $conf{'default_sound'} = $user_config{'default_sound'}; } else { print "Error: $user_config{default_sound}, no such file or directory.\n"; } } foreach (1..10) { if (exists($user_config{"sound${_}"})) { if (-f "$user_config{sound${_}}") { $conf{"sound${_}"} = $user_config{"sound${_}"}; } else { print "Error: $user_config{sound${_}}, no such file or directory.\n"; $conf{"sound${_}"} = $conf{'default_sound'}; } } } %user_config = (); return 1; } ## End sub parse_config() ##-----------------------------------------------------------------------## ##-----------------------------------------------------------------------## ## sub sig_handler() ## This function handles the TERM/INT signals. Basically, it ## causes the program to close its connections and die. sub sig_handler { if (defined($dsp)) { $dsp->clear(); $dsp->close(); } close($FH) if defined($FH); exit(0); } ## End sub sig_handler() ##-----------------------------------------------------------------------## ##-----------------------------------------------------------------------## ## sub bin2dec() ## Converts binary into decimal string. sub bin2dec { return unpack("N", pack("B32", substr("0" x 32 . shift, -32))); } ## End sub bin2dec() ##-----------------------------------------------------------------------## ## _EOF_ ##