#!/usr/bin/perl # # AST_send_action_child.pl version 2.14 # # Part of the Asterisk Central Queue System (ACQS) # # DESCRIPTION: # This script is spawned every time an action is to be executed by the main # AST_manager_send.pl script and sends actions blindly to the Asterisk manager # # SUMMARY: # This program was designed as the blind-send part of the ACQS. It's job is to # be spawned by the AST_manager_send.pl script lookup the record in the MySQL DB # to be executed. connect to the manager interface, send the action and logoff # then exit. # # Copyright (C) 2018 Matt Florell LICENSE: AGPLv2 # # CHANGES # 50810-1547 - Added database server variable definitions lookup # 50823-1527 - Altered commandline debug options with debug printouts # 50902-1032 - Changed default logging to fulllog # 60718-0909 - Changed to DBI by Marin Blu # 60718-1024 - Changed to use /etc/astguiclient.conf for configs # 60814-1720 - Added option for no logging to file # 60817-1244 - Removed all DB calls and config file open to reduce footprint # 61004-1728 - Added ability to parse volume control and lookup meetme IDs # 61220-1720 - Optimize and clean code # 61221-1942 - Additional modification to structure # 80418-0927 - Added man_id to logging output for better tracking, raised sleep times # 100625-1141 - Added waitfors after orginate and logout to fix broken pipe errors in asterisk # 100625-1206 - Use strict is a performance hit and should only be uncommented for debugging # 130108-1714 - Changes for Asterisk 1.8 compatibility # 150610-1200 - Added support for AMI version 1.3 # 180403-1544 - Updated telnet login process to work with newer Asterisk versions # $|++; # use strict is a performance hit and should only be uncommented for debugging #use strict; # use Getopt::Long; use Net::Telnet; ### Initialize date/time vars ### my $secX = time(); #Start time ### Initialize run-time variables ### my ($CLOhelp, $SYSLOG, $PATHlogs, $telnet_host, $telnet_port, $ASTmgrUSERNAME); my ($ASTmgrSECRET, $ASTmgrUSERNAMEsend, $man_id, $action, $cmd_line_b, $cmd_line_c); my ($cmd_line_d, $cmd_line_e, $cmd_line_f, $cmd_line_g, $cmd_line_h); my ($cmd_line_i, $cmd_line_j, $cmd_line_k, $DB, $DBX); my $FULL_LOG = 1; ### begin parsing run-time options ### if (scalar @ARGV) { GetOptions('help!' => \$CLOhelp, 'SYSLOG!' => \$SYSLOG, 'PATHlogs=s' => \$PATHlogs, 'telnet_host=s' => \$telnet_host, 'telnet_port=s' => \$telnet_port, 'ASTmgrUSERNAME=s' => \$ASTmgrUSERNAME, 'ASTmgrSECRET=s' => \$ASTmgrSECRET, 'ASTmgrUSERNAMEsend=s' => \$ASTmgrUSERNAMEsend, 'man_id=s' => \$man_id, 'action=s' => \$action, 'cmd_line_b=s' => \$cmd_line_b, 'cmd_line_c=s' => \$cmd_line_c, 'cmd_line_d=s' => \$cmd_line_d, 'cmd_line_e=s' => \$cmd_line_e, 'cmd_line_f=s' => \$cmd_line_f, 'cmd_line_g=s' => \$cmd_line_g, 'cmd_line_h=s' => \$cmd_line_h, 'cmd_line_i=s' => \$cmd_line_i, 'cmd_line_j=s' => \$cmd_line_j, 'cmd_line_k=s' => \$cmd_line_k, 'debug!' => \$DB, 'debugX!' => \$DBX, 'fulllog!' => \$FULL_LOG); $DB = 1 if ($DBX); if ($DB) { print "\n----- DEBUGGING -----\n\n"; print "\n----- SUPER-DUPER DEBUGGING -----\n\n" if ($DBX); print " SYSLOG: $SYSLOG\n" if ($SYSLOG); print " telnet_host: $telnet_host\n" if ($telnet_host); print " telnet_port: $telnet_port\n" if ($telnet_port); print " ASTmgrUSERNAME: $ASTmgrUSERNAME\n" if ($ASTmgrUSERNAME); print " ASTmgrSECRET: $ASTmgrSECRET\n" if ($ASTmgrSECRET); print " ASTmgrUSERNAMEsend: $ASTmgrUSERNAMEsend\n" if ($ASTmgrUSERNAMEsend); print " man_id: $man_id\n" if ($man_id); print " cmd_line_b: $cmd_line_b\n" if ($cmd_line_b); print " cmd_line_c: $cmd_line_c\n" if ($cmd_line_c); print " cmd_line_d: $cmd_line_d\n" if ($cmd_line_d); print " cmd_line_e: $cmd_line_e\n" if ($cmd_line_e); print " cmd_line_f: $cmd_line_f\n" if ($cmd_line_f); print " cmd_line_g: $cmd_line_g\n" if ($cmd_line_g); print " cmd_line_h: $cmd_line_h\n" if ($cmd_line_h); print " cmd_line_i: $cmd_line_i\n" if ($cmd_line_i); print " cmd_line_j: $cmd_line_j\n" if ($cmd_line_j); print " cmd_line_k: $cmd_line_k\n" if ($cmd_line_k); print "\n"; } if ($CLOhelp) { print "allowed run time options:\n"; print " [--help] = this help screen\n"; print " [--SYSLOG] = whether to log actions or not\n"; print "required flags:\n"; print " [--PATHlogs] = logs directory path\n"; print " [--telnet_host] = IP address to connect to Asterisk Manager\n"; print " [--telnet_port] = port to connect to Asterisk Manager\n"; print " [--ASTmgrUSERNAME] = username for Asterisk Manager login\n"; print " [--ASTmgrSECRET] = secret or password for Asterisk Manager login\n"; print " [--ASTmgrUSERNAMEsend] = username specific for sending actions for Asterisk Manager login\n"; print " [--man_id] = ID of the action in the vicidial_manager table\n"; print " [--action] = type of manager action to send\n"; print " [--cmd_line_X] = lines to send to Manager after action\n"; print " X replaced with b-k (10 lines)\n"; print "\n"; print "You may prefix an option with 'no' to disable it.\n"; print " ie. --noSYSLOG or --noFULLLOG\n"; exit 0; } } if ($action) { $PATHlogs =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_b =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_c =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_d =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_e =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_f =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_g =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_h =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_i =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_j =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; $cmd_line_k =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; if ($DB) { print " SYSLOG: $SYSLOG\n" if ($SYSLOG); print " telnet_host: $telnet_host\n" if ($telnet_host); print " telnet_port: $telnet_port\n" if ($telnet_port); print " ASTmgrUSERNAME: $ASTmgrUSERNAME\n" if ($ASTmgrUSERNAME); print " ASTmgrSECRET: $ASTmgrSECRET\n" if ($ASTmgrSECRET); print " ASTmgrUSERNAMEsend: $ASTmgrUSERNAMEsend\n" if ($ASTmgrUSERNAMEsend); print " man_id: $man_id\n" if ($man_id); print " cmd_line_b: $cmd_line_b\n" if ($cmd_line_b); print " cmd_line_c: $cmd_line_c\n" if ($cmd_line_c); print " cmd_line_d: $cmd_line_d\n" if ($cmd_line_d); print " cmd_line_e: $cmd_line_e\n" if ($cmd_line_e); print " cmd_line_f: $cmd_line_f\n" if ($cmd_line_f); print " cmd_line_g: $cmd_line_g\n" if ($cmd_line_g); print " cmd_line_h: $cmd_line_h\n" if ($cmd_line_h); print " cmd_line_i: $cmd_line_i\n" if ($cmd_line_i); print " cmd_line_j: $cmd_line_j\n" if ($cmd_line_j); print " cmd_line_k: $cmd_line_k\n" if ($cmd_line_k); } $telnet_port = '5038' if (!$telnet_port); ### connect to asterisk manager through telnet my $tn = new Net::Telnet (Port => $telnet_port, Prompt => '/\r\n/', Output_record_separator => '', Errmode => "return"); ##### uncomment both lines below for telnet log # $LItelnetlog = "$PATHlogs/send_child_telnet_log.txt"; # $fh = $tn->dump_log("$LItelnetlog"); if (length($ASTmgrUSERNAMEsend) > 3) {$telnet_login = $ASTmgrUSERNAMEsend;} else {$telnet_login = $ASTmgrUSERNAME;} $tn->open("$telnet_host"); $tn->waitfor('/Asterisk Call Manager\//'); # get the AMI version number $ami_version = $tn->getline(Errmode => Return, Timeout => 1,); $ami_version =~ s/\n//gi; if ($DB) {print "----- AMI Version $ami_version -----\n";} # Login $tn->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); $tn->waitfor('/Authentication accepted/'); # waitfor auth accepted $tn->buffer_empty; if ($cmd_line_b =~ /XXYYXXYYXXYYXX/) { # Action: Command # Command: meetme list 8600051 # # Response: Follows # Privilege: Command # User #: 03 261 261iax Channel: IAX2/261iax-13 (unmonitored) # 1 users in that conference. # --END COMMAND-- my $meetme_command = "Action: Command\nCommand: meetme list $cmd_line_k\n\n"; print nowDate() . "|$SYSLOG|\n$meetme_command"; my $participant; my @list_meetme = $tn->cmd(String => $meetme_command, Prompt => '/--END COMMAND-.*/'); foreach my $meetme (@list_meetme) { if ($meetme =~ /$cmd_line_j /i) { $meetme =~ s/User \#: //gi; my @participants = split(/ /, $meetme); $participant = ($participants[0] + 0); } # print "$participant|$cmd_line_j|$meetme"; } $cmd_line_b =~ s/XXYYXXYYXXYYXX/$participant/gi if ($participant > 0); $cmd_line_j = ''; $cmd_line_k = ''; } my $originate_command; $originate_command .= "Action: $action\n"; #if (length($cmd_line_b)>3) {$originate_command .= "$cmd_line_b\n";} $originate_command .= $cmd_line_b . "\n" if ($cmd_line_b); $originate_command .= $cmd_line_c . "\n" if ($cmd_line_c); $originate_command .= $cmd_line_d . "\n" if ($cmd_line_d); $originate_command .= $cmd_line_e . "\n" if ($cmd_line_e); $originate_command .= $cmd_line_f . "\n" if ($cmd_line_f); $originate_command .= $cmd_line_g . "\n" if ($cmd_line_g); $originate_command .= $cmd_line_h . "\n" if ($cmd_line_h); $originate_command .= $cmd_line_i . "\n" if ($cmd_line_i); $originate_command .= $cmd_line_j . "\n" if ($cmd_line_j); $originate_command .= $cmd_line_k . "\n" if ($cmd_line_k); $originate_command .= "\n"; print nowDate() . "|$SYSLOG|$man_id|\n$originate_command"; my $event_string = $man_id . "|0|" . $SYSLOG . "|"; $event_string .= "\n" . $originate_command; eventLogger($PATHlogs,'full',$event_string) if ($FULL_LOG and $SYSLOG); my @list_channels = $tn->cmd(String => $originate_command, Prompt => '/.*/'); $tn->waitfor(Match => '/Response:.*\n/', Timeout => 10); $tn->waitfor(Match => '/Message:.*\n\n/', Timeout => 10); my $data1; # ? Useless ? if ($FULL_LOG and $SYSLOG) { my $event_string = $man_id . "|1|" . $data1 . "|"; foreach my $channel (@list_channels) { $event_string .= $channel; } my $read_input_buf = $tn->get(Errmode => "return", Timeout => 1); $event_string .= $read_input_buf; eventLogger($PATHlogs,'full',$event_string); } $tn->buffer_empty; #@hangup = $tn->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); $tn->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); $tn->buffer_empty; $tn->waitfor(Match => '/Message:.*\n\n/', Timeout => 15); if ($FULL_LOG and $SYSLOG) { my $event_string = $man_id . "|2|" . $data1 . "|"; foreach my $channel (@list_channels) { $event_string .= $channel; } my $read_input_buf = $tn->get(Errmode => "return", Timeout => 1); $event_string .= $read_input_buf; eventLogger($PATHlogs,'full',$event_string); } $tn->buffer_empty; $tn->close; } my $secZ = time(); my $script_time = ($secZ - $secX); if ($DB) {print "DONE execute time: $script_time seconds\n";} exit 0; # Program ends. ### Start of subs. # eventLogger usage: # eventLgger($LogFileDir, $LogType, $EventString); # Requires: # $LogFilePath : Directory where log files are. # $LogType : Type of log, ie process, send, launch, full # $EventString : String to record in log. sub eventLogger { my ($path,$type,$string) = @_; open(LOG, ">>" . $path . "/action_" . $type . "." . logDate()) || die "Can't open " . $path . "/action_" . $type . "." . logDate() . ": " . $! . "\n"; print LOG nowDate() . "|" . $string . "|\n"; close(LOG); } # getTime usage: # getTime($SecondsSinceEpoch); # Options: # $SecondsSinceEpoch : Request time in seconds, defaults to current date/time. # Returns: # ($sec, $min, $hour. $day, $mon, $year) sub getTime { my ($tms) = @_; $tms = time unless ($tms); my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($tms); $year += 1900; $mon++; $mon = "0" . $mon if ($mon < 10); $mday = "0" . $mday if ($mday < 10); $min = "0" . $min if ($min < 10); $sec = "0" . $sec if ($sec < 10); return ($sec,$min,$hour,$mday,$mon,$year); } # nowDate usage: # nowDate($SecondsSinceEpoch); # Options: # $SecondsSinceEpoch : Request time in seconds, defaults to current date/time. # Returns: # scalar date/time string (MySQL formatted) ie "2007-01-01 00:00:00" sub nowDate { my ($tms) = @_; my($sec,$min,$hour,$mday,$mon,$year) = getTime($tms); return $year.'-'.$mon.'-'.$mday.' '.$hour.':'.$min.':'.$sec; } # logDate usage: # logDate($SecondsSinceEpoch); # Options: # $SecondsSinceEpoch : Request time in seconds, defaults to current date/time. # Returns: # scalar date string ie "2007-01-01" sub logDate { my ($tms) = @_; my($sec,$min,$hour,$mday,$mon,$year) = getTime($tms); return $year . '-' . $mon . '-' . $mday; } ### End of subs