aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/scripts/astcli
blob: 0ea245f14170670c8e5191515021c47c6d1fb1c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/perl -w

use strict;
use IO::Socket;
use Getopt::Long;

# Created by: David Van Ginneken
# Bird's the Word Technologies
# davevg@btwtech.com
#
# And distributed under the terms of the GPL
#
my ($user, $pw, $host, $port, $interactive, $save) = (undef, undef, 'localhost', 5038, 0, 0);
my $EOL = "\r\n"; # Standard End of Line
my @commands;
process_credentials('/etc/astcli.conf');
process_credentials("$ENV{HOME}/.astcli") if defined $ENV{HOME};
GetOptions("username=s" => \$user, "secret=s" => \$pw, "host=s" => \$host, "port=s" => \$port, "readline" => \$interactive, "write" => \$save);

$|++; # Auto Flush Output
my $action = join(" ", @ARGV);

&usage if (!defined $user || !defined $pw);
my $tc = new IO::Socket::INET(
		PeerAddr => $host,
		PeerPort => $port,
		Timeout => 30,
		Proto => 'tcp'
) or die "Could not connect to Host: $host on port $port\n";
if (my $error = login()) {
	print STDERR $error;
	exit 1;
};

if ($save) {
	if (-d $ENV{HOME}) {
		open DEFAULT, ">$ENV{HOME}/.astcli";
		print DEFAULT "username=$user\n" if $user;
		print DEFAULT "password=$pw\n" if $pw;
		print DEFAULT "hostname=$host\n" if $host;
		print DEFAULT "portno=$port\n" if $port;
		close DEFAULT;
	}
}

# Send a single command to the manager connection handle (global $tc).
# Assumes things always work well :-)
sub send_command($) {
	my $command = shift;
	$tc->send('Action: Command' . $EOL);
	$tc->send("Command: $command" . $EOL);
	$tc->send($EOL);
	my $response = '';
	while (<$tc>) {
		if ($_ =~ /--END COMMAND--/) {
			$_ =~ s/--END COMMAND--\s*//;
			$response .= $_;
			last;
		}
		$response .= $_;
	}
	$response =~ s/Privilege: Command$EOL//;
	$response =~ s/Response: Follows$EOL//;
	return $response;
}

sub login {
	my ($response, $message);
	$tc->send("Action: Login" . $EOL);
	$tc->send("Username: $user" . $EOL);
	$tc->send("Secret: $pw" . $EOL);
	$tc->send("Events: off" . $EOL);
	$tc->send($EOL);
	while (<$tc>) {
		last if $_ eq $EOL;
		$_ =~ s/$EOL//g;
		($response) = $_ =~ /^Response: (.*?)$/ if $_ =~ /^Response:/;
		($message) = $_ =~ /^Message: (.*?)$/ if $_ =~ /^Message:/;
	}
	return 0 if $response eq 'Success';
	return $message;
}

sub logoff {
	my ($response, $message);
	$tc->send("Action: Logoff" . $EOL . $EOL);
	return 1;
}

# If the user asked to send commands from standard input:
if ($action eq '-' || !defined $action || $action eq '') {
	if ($interactive) {
		eval { require Term::ReadLine;};
		$interactive = scalar($@) ? 0 : 1;
		print STDERR "Falling back to standard mode, Unable to load Term::Readline for readline mode\n" unless $interactive;
	}
	if ($interactive) {
		my $term = new Term::ReadLine 'Command Line Interface';
		my $prompt = "$host*CLI> ";
		my $attribs = $term->Attribs;
		$attribs->{completion_function} = \&tab_completion;
		while (defined($_ = $term->readline($prompt))) {
			(logoff() and exit) if $_ =~ /exit|quit/; # Give them a way to exit the "terminal"
			print send_command($_) if $_ !~ m/^\s*$/;
		}	
	} else {
		while (<>) {
			chomp;
			(logoff() and exit) if $_ =~ /exit|quit/; # If someone accidentally ends up here, let them exit
			print send_command($_);
		}
	}
	exit 0;
}

# Otherwise just send the command:
print send_command($action);

# parses a configuration file into the global $user and $pw.
sub process_credentials {
	# Process the credentials found..
	my $file = shift;
	# silently fail if we can't read the file:
	return unless (-r $file);
	open (my $fh, "<$file") or return;
	while (<$fh>) {
		chomp;
		(undef,$user) = split(/[,=]/, $_) if $_ =~ /user(name)?[,=]/i;
		(undef,$pw) = split(/[,=]/, $_) if $_ =~ /(secret|passw(or)?d|pwd?)[,=]/i;
		(undef,$host) = split(/[,=]/, $_) if $_ =~ /host(name)?[,=]/i;
		(undef,$port) = split(/[,=]/, $_) if $_ =~ /port(num|no)?[,=]/i;
	}
	close ($fh);
}

sub usage {
	print STDERR "astcli [<options>] [<cli-command>|-]\n";
	print STDERR "       -u <name> - Connect as username <name>\n";
	print STDERR "       -s <pw>   - Connect with secret <pw>\n";
	print STDERR "       -h <host> - Connect to host <host> [localhost]\n";
	print STDERR "       -p <port> - Connect on TCP port <port> [5038]\n";
	print STDERR "       -r        - Start a readline session for interactivity\n";
	print STDERR "       -w        - Save connection options in a configuration file\n";
	print STDERR "   You may specify the command as '-' to take commands from stdin.\n";
	exit;
}

sub tab_completion {
    my ($word, $buffer, $offset) = @_;
	my %items;
	my $lastword = '';
	if ($word eq '') {
		$buffer =~ m/(\S+)\s?$/;
		$lastword = $1;
		#print STDERR "\n\nlastword=\"$lastword\"\n";
	}

	my $res = send_command("_command matchesarray \"$buffer\" \"$word\"");
	foreach my $item (split /\s+/, $res) {
		$items{$item}++ unless ($item eq '_EOF_' or $item eq '' or $item eq $lastword);
	}
		
	#print STDERR "\nword=\"$word\" buffer=\"$buffer\" offset=\"$offset\" res=\"$res\"\n";

	return sort keys %items;
}