astcli 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/perl -w
  2. use strict;
  3. use IO::Socket;
  4. use Getopt::Long;
  5. # Created by: David Van Ginneken
  6. # Bird's the Word Technologies
  7. # davevg@btwtech.com
  8. #
  9. # And distributed under the terms of the GPL
  10. #
  11. my ($user, $pw, $host, $port, $interactive, $save) = (undef, undef, 'localhost', 5038, 0, 0);
  12. my $EOL = "\r\n"; # Standard End of Line
  13. my @commands;
  14. process_credentials('/etc/astcli.conf');
  15. process_credentials("$ENV{HOME}/.astcli") if defined $ENV{HOME};
  16. GetOptions("username=s" => \$user, "secret=s" => \$pw, "host=s" => \$host, "port=s" => \$port, "readline" => \$interactive, "write" => \$save);
  17. $|++; # Auto Flush Output
  18. my $action = join(" ", @ARGV);
  19. &usage if (!defined $user || !defined $pw);
  20. my $tc = new IO::Socket::INET(
  21. PeerAddr => $host,
  22. PeerPort => $port,
  23. Timeout => 30,
  24. Proto => 'tcp'
  25. ) or die "Could not connect to Host: $host on port $port\n";
  26. if (my $error = login()) {
  27. print STDERR $error;
  28. exit 1;
  29. };
  30. if ($save) {
  31. if (-d $ENV{HOME}) {
  32. open DEFAULT, ">$ENV{HOME}/.astcli";
  33. print DEFAULT "username=$user\n" if $user;
  34. print DEFAULT "password=$pw\n" if $pw;
  35. print DEFAULT "hostname=$host\n" if $host;
  36. print DEFAULT "portno=$port\n" if $port;
  37. close DEFAULT;
  38. }
  39. }
  40. # Send a single command to the manager connection handle (global $tc).
  41. # Assumes things always work well :-)
  42. sub send_command($) {
  43. my $command = shift;
  44. $tc->send('Action: Command' . $EOL);
  45. $tc->send("Command: $command" . $EOL);
  46. $tc->send($EOL);
  47. my $response = '';
  48. while (<$tc>) {
  49. if ($_ =~ /--END COMMAND--/) {
  50. $_ =~ s/--END COMMAND--\s*//;
  51. $response .= $_;
  52. last;
  53. }
  54. $response .= $_;
  55. }
  56. $response =~ s/Privilege: Command$EOL//;
  57. $response =~ s/Response: Follows$EOL//;
  58. return $response;
  59. }
  60. sub login {
  61. my ($response, $message);
  62. $tc->send("Action: Login" . $EOL);
  63. $tc->send("Username: $user" . $EOL);
  64. $tc->send("Secret: $pw" . $EOL);
  65. $tc->send("Events: off" . $EOL);
  66. $tc->send($EOL);
  67. while (<$tc>) {
  68. last if $_ eq $EOL;
  69. $_ =~ s/$EOL//g;
  70. ($response) = $_ =~ /^Response: (.*?)$/ if $_ =~ /^Response:/;
  71. ($message) = $_ =~ /^Message: (.*?)$/ if $_ =~ /^Message:/;
  72. }
  73. return 0 if $response eq 'Success';
  74. return $message;
  75. }
  76. sub logoff {
  77. my ($response, $message);
  78. $tc->send("Action: Logoff" . $EOL . $EOL);
  79. return 1;
  80. }
  81. # If the user asked to send commands from standard input:
  82. if ($action eq '-' || !defined $action || $action eq '') {
  83. if ($interactive) {
  84. eval { require Term::ReadLine;};
  85. $interactive = scalar($@) ? 0 : 1;
  86. print STDERR "Falling back to standard mode, Unable to load Term::Readline for readline mode\n" unless $interactive;
  87. }
  88. if ($interactive) {
  89. my $term = new Term::ReadLine 'Command Line Interface';
  90. my $prompt = "$host*CLI> ";
  91. my $attribs = $term->Attribs;
  92. $attribs->{completion_function} = \&tab_completion;
  93. while (defined($_ = $term->readline($prompt))) {
  94. (logoff() and exit) if $_ =~ /exit|quit/; # Give them a way to exit the "terminal"
  95. print send_command($_) if $_ !~ m/^\s*$/;
  96. }
  97. } else {
  98. while (<>) {
  99. chomp;
  100. (logoff() and exit) if $_ =~ /exit|quit/; # If someone accidentally ends up here, let them exit
  101. print send_command($_);
  102. }
  103. }
  104. exit 0;
  105. }
  106. # Otherwise just send the command:
  107. print send_command($action);
  108. # parses a configuration file into the global $user and $pw.
  109. sub process_credentials {
  110. # Process the credentials found..
  111. my $file = shift;
  112. # silently fail if we can't read the file:
  113. return unless (-r $file);
  114. open (my $fh, "<$file") or return;
  115. while (<$fh>) {
  116. chomp;
  117. (undef,$user) = split(/[,=]/, $_) if $_ =~ /user(name)?[,=]/i;
  118. (undef,$pw) = split(/[,=]/, $_) if $_ =~ /(secret|passw(or)?d|pwd?)[,=]/i;
  119. (undef,$host) = split(/[,=]/, $_) if $_ =~ /host(name)?[,=]/i;
  120. (undef,$port) = split(/[,=]/, $_) if $_ =~ /port(num|no)?[,=]/i;
  121. }
  122. close ($fh);
  123. }
  124. sub usage {
  125. print STDERR "astcli [<options>] [<cli-command>|-]\n";
  126. print STDERR " -u <name> - Connect as username <name>\n";
  127. print STDERR " -s <pw> - Connect with secret <pw>\n";
  128. print STDERR " -h <host> - Connect to host <host> [localhost]\n";
  129. print STDERR " -p <port> - Connect on TCP port <port> [5038]\n";
  130. print STDERR " -r - Start a readline session for interactivity\n";
  131. print STDERR " -w - Save connection options in a configuration file\n";
  132. print STDERR " You may specify the command as '-' to take commands from stdin.\n";
  133. exit;
  134. }
  135. sub tab_completion {
  136. my ($word, $buffer, $offset) = @_;
  137. my %items;
  138. my $lastword = '';
  139. if ($word eq '') {
  140. $buffer =~ m/(\S+)\s?$/;
  141. $lastword = $1;
  142. #print STDERR "\n\nlastword=\"$lastword\"\n";
  143. }
  144. my $res = send_command("_command matchesarray \"$buffer\" \"$word\"");
  145. foreach my $item (split /\s+/, $res) {
  146. $items{$item}++ unless ($item eq '_EOF_' or $item eq '' or $item eq $lastword);
  147. }
  148. #print STDERR "\nword=\"$word\" buffer=\"$buffer\" offset=\"$offset\" res=\"$res\"\n";
  149. return sort keys %items;
  150. }