#!/usr/bin/perl
use warnings;
use strict;
use IO::Socket; # so we can hear those broadcasts
use IO::Handle; # for log flush

use POSIX; # big honkin module, but we need some of it


# Hopefully thisn (recd) will become a lightweight daemon to use sox rec via remote control.
# I'm taking this approach because there seems to be an issue spawning rec from inside a GTK callback.
# It may have some more-general uses.  To that end, I'll use external vs AF_LINUX internal sockets.
# UDP seems easy to program and "reliable enough" for this...since only commands go over the socket.

# the idea would be that you can set a base path for the result if you don't like my default.
# ditto a command.  We'll use a time stamp for the filename.  More features might be added.
# the basic format will be "command:value" as a UDP message.
# examples:  path:/home/user/bin  or opt: -c2 -q  (note space before and after this data)
# obvious:  go, stop (with or without the :)  die for debuging

# rec (sox) appears to not bitch about a file already existing.

my $debug = 0; # true creates a log file in hardcoded dir(!)

my $timetodie; # on a long enough horizon
# basic record stuff
my $soxpid;

### time stuff
my $timestring;
my @rawtime;

# socket stuff
my $pid; # for daemonizing
my $msgsock; # the socket
my $portno = 53129;  # joke, sister's birthday, but likely to be unique
my $maxtoread = 256; # arbitrary limit
my $them; # who sent this in case we care later
my $theirport;
my $theirip;
my $msg; # what we've been sent

# log file stuff
my $log; # filehandle in the new days can be a variable
my $logstring;
my $logdir = "/home/odroid/bin"; #@@@ machine-user-secific, oops

# command stuff
my $remcmd;	# command from 'net post split
my $remdata;	# data field from above
my $audiopath = "/home/odroid/testaud.mp3"; # for testing at low disk use in odroid
my $opencmd = '| rec'; # command base
my $options = ' -c2 -q '; #@@@ note, this is the one that has spaces on both ends

###########################################################
sub loggit
{ # write whatever we were called with, prepend time, suffix newline
  # put result in log window and file
 return unless $debug; # logfile is about the only way to tell ourself about stuff
 updatetime(); # get current time-string in $timestring
 $logstring = "$timestring" . "\t" . shift . "\n";
 print $log $logstring; # write to file
}

###########################################################
sub updatetime
{ # create global $timestring for "now" in YY_MM_DD_HH:MM:SS format
  # saves me from having to remember this shit all the time
 @rawtime = localtime(); # get array of time values
 $timestring = sprintf ("%02d_%02d_%02d_%02d:%02d:%02d",
	$rawtime[5]%100,$rawtime[4]+1,$rawtime[3],$rawtime[2],$rawtime[1],$rawtime[0]);
} # was that fiddly and opaque enough?
###########################################################
sub openlogfile
{ # create a file named "current time" for logging to - looks easy?
  #dirname($0) is path to program w/o program name on it or trailing slash
 updatetime(); # use current timestring as logfilename
 open($log, ">","$logdir" ."/$timestring".'.log') or die "couldn't open logfile:$!\n";
 $log->autoflush; # slower but we want logging right up till instant of death, right away
} # about the only time "die" is a good idea in this program, debug goes to log
###########################################################
sub go {
	my $cmdstr = $opencmd . $options . $audiopath;
	$soxpid = open (my $sox,$cmdstr);
	loggit "soxpid was $soxpid\t command was:$cmdstr";

}
###########################################################
sub stop {
	if ($soxpid) # one way to kill, we could send it ctrl-c via pipe?
	 {
	  if (kill 'INT',$soxpid) {$soxpid = 0;} else {loggit "failed kill PID:$soxpid\n";}
# the idea being, leave the pid in existance if the process is, for a later try	  	
	 } else {
	  loggit "no soxpid to kill!";
	 }
}
###########################################################
 sub setpath { #@@@ needs some kind of testing for danger here
	$audiopath = $remdata; 
}
###########################################################
sub setopts { #@@@ needs testing for validity
	$options = $remdata;
}
###########################################################
###########################################################
# turned out to be a bad way to handle this in modern perl
#sub REAPER {
# my$stiff;
# while(($stiff = waitpid(-1,&WNOHANG) >0)) {;}
# $SIG{CHLD} = \&REAPER;
#}
###########################################################
sub signal_handler 
{ 
 $timetodie = 1; # die if anyone asks us to via signal
}
###########################################################
sub init { # start the ball rolling, set things up
	if ($debug) {openlogfile();} # the main way a daemon can tell you what's going on
	setpgrp(0,0); # try to be as rooty as we can be
	$pid = fork(); # we're going to detach from whoever started us
	exit if $pid; # parent dies
	die "Couldn't fork: $!" unless defined($pid);
	POSIX::setsid() or die "Can't start new session: $!";
	close (STDIN); # never use it anyway
	close (STDOUT) unless $debug;
	close (STDERR) unless $debug;
 	$SIG{CHLD} = 'IGNORE'; # ignore is better, perl handles this well now 
	# instead of \&REAPER; # even open() can sometimes make a zombie
	$SIG{TERM} = \&signal_handler; # die
	$msgsock = IO::Socket::INET->new(LocalPort => $portno, Proto => "udp")
 		or die ("couldn't bind to port:$!");
	loggit ("started.  socket ref:$msgsock");
}


###########################################################
sub docmd { # figure out what to do and do it
 # @@@ theoretically, we could add all kinds of error checking, tainting etc here...
 if ('stop' eq $remcmd) {
  stop();
  return;
 }	
 if ('go' eq $remcmd) {
  go();
  return;
 } 
 if ('die' eq $remcmd) {
  stop(); # just in case - creates incorrect message in log if rec wasn't running.
  exit; # really die
 }
 if ('path' eq $remcmd) {
  setpath();
  return
 }
 if ('opt' eq $remcmd) {
  setopts();
  return;
 } 
}
###########################################################
############   MAIN #######################################
###########################################################
 init(); # fork, kill parent, set up socket and logfile etc

 until ($timetodie) { # wait for a command, do it, and go back to waiting
	  $them = $msgsock->recv($msg,$maxtoread); # block until message
	  chomp $msg; # liberal in what we accept philosophy
	  ($theirport, $theirip) = sockaddr_in($them); # log who told us
 	  $theirip = inet_ntoa($theirip); # for log/debug
 	  loggit "message:$msg from:$theirip:$theirport"; # what was said by who
	  ($remcmd,$remdata) = split /:/,$msg,2; # some commands have a data field
	  $remcmd = lc $remcmd; # save extra compares or lc's later
	  docmd(); # slightly cleaner way
}

END { # clean house

# port objects auto-close on undef, so no need (and better than a close)
 loggit "We died clean-happy. pid:$pid"; # if non-zero, it's the fork, if zero, it's the daemon
# close $log; # should auto-close anyway, but
}


