Add support for non-interactive use of Console

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/ec/trunk@37751 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2014-03-18 15:23:33 +00:00
parent 174cfb3841
commit ba042516d1
5 changed files with 298 additions and 53 deletions

View file

@ -1,3 +1,11 @@
2014-03-18 Richard Frith-Macdonald <rfm@gnu.org>
* ECCL.h:
* EcCommand.m:
* EcConsole.m:
* GNUmakefile:
Extend Console tool to support non-interactive use for scripting etc.
2014-02-28 Richard Frith-Macdonald <rfm@gnu.org>
* EcControl.m:

50
ECCL.h
View file

@ -39,6 +39,56 @@
</p>
</section>
<section>
<heading>The Control command line tool</heading>
<p>Based around the [EcControl] class, this server process provides
a central command/control point and acts as the SNMP agent for
integration of a cluster of Objective-C based processes.
</p>
</section>
<section>
<heading>The Command command line tool</heading>
<p>Based around the [EcCommand] class, this server process provides
the configuration/logging/launch facilities for all processes on
a single machine. It obtains centralised configuration from the
Control server and sends logging, alerts and alarms to that server.
</p>
</section>
<section>
<heading>The Console command line tool</heading>
<p>Based around the [EcConsole] class, this process provides an
interactive command-line based interface for controlling the
entire system via the Control server.<br />
It may also be used in a non-interactive mode to send commands to
any process and wait for responses. In non-interactive mode the
tool uses the following additional
command-line-arguments/user-default-keys and/or
environment variables:
</p>
User (or the ConsoleUser environment variable) specifies the
user for whom the tool is performing an action.<br />
Pass (or the ConsolePass environment variable) specifies the
password for the user login to the Control server.<br />
Line (or the ConsoleLine environment variable) specifies the
command line to be used (as if type in interactively).
If the command line is sent, the process exit status is 0, but
if a failure occurs the status is 1 (except as noted below).<br />
Wait (or the ConsoleWait environment variable) specifies the
number of seconds to wait for the result of the command.
If this timeout occurs, the process exit status is 2.<br />
Want (or the ConsoleWant environment variable) specifies the
regular expression to match a single line success response to the
command.<br />
Fail (or the ConsoleFail environment variable) specifies the regular
expression to match a single line failure response to the command.
If this response is matched, the process exit status is 3.<br />
Quiet (or the ConsoleQuiet environment variable) is a boolean
which, if true, will suppress any output while waiting for a
response.
</section>
<section>
<heading>The AlarmTool command line tool</heading>
<p>The AlarmTool command line tool provides a mechanism to raise and

View file

@ -1213,7 +1213,7 @@ static NSString* cmdWord(NSArray* a, unsigned int pos)
NSRange r;
id o;
if (nil == config)
if (nil == config || 0 == [name length])
{
return nil; // Not available
}

View file

@ -68,6 +68,8 @@ static BOOL commandIsRepeat (NSString *string)
NSString *user;
NSString *pass;
NSString *rnam;
NSRegularExpression *want;
NSRegularExpression *fail;
id server;
int pos;
}
@ -82,6 +84,7 @@ static BOOL commandIsRepeat (NSString *string)
- (void) didWrite: (NSNotification*)notification;
- (void) doCommand: (NSMutableArray*)words;
- (void) timedOut;
- (void) waitEnded: (NSTimer*)t;
@end
@ -96,6 +99,9 @@ static BOOL commandIsRepeat (NSString *string)
- (void) cmdQuit: (NSInteger)sig
{
[timer invalidate];
timer = nil;
/* Attempt to output an exit message, but our tereminal may have gone away
* so we ignore exceptions during that.
*/
@ -163,8 +169,7 @@ static BOOL commandIsRepeat (NSString *string)
{
if (server && [(NSDistantObject*)server connectionForProxy] == conn)
{
[server release];
server = nil;
DESTROY(server);
NSLog(@"Lost connection to Control server.");
[self cmdQuit: 0];
}
@ -179,6 +184,8 @@ static BOOL commandIsRepeat (NSString *string)
[host release];
[name release];
[local release];
[want release];
[fail release];
[data release];
if (timer)
{
@ -202,42 +209,6 @@ static BOOL commandIsRepeat (NSString *string)
#endif
}
- (void)processReadLine:(NSString *)_line
{
NSAutoreleasePool *arp;
NSMutableArray *words;
NSEnumerator *wordsEnum;
NSString *word;
if (_line == nil)
{
[self cmdQuit: 0];
}
_line = [_line stringByTrimmingSpaces]; // Remove trailing newline
if ([_line length] == 0)
{
return;
}
arp = [[NSAutoreleasePool alloc] init];
/* setup word array */
words = [NSMutableArray arrayWithCapacity: 16];
wordsEnum = [[_line componentsSeparatedByString: @" "] objectEnumerator];
while ((word = [wordsEnum nextObject]) != nil)
{
[words addObject: [word stringByTrimmingSpaces]];
}
/* invoke server */
[self doCommand: words];
[arp release];
}
#if defined(HAVE_LIBREADLINE)
- (char **)complete:(const char *)_text range:(NSRange)_range
@ -598,6 +569,42 @@ static BOOL commandIsRepeat (NSString *string)
}
}
- (void) doLine:(NSString *)_line
{
NSAutoreleasePool *arp;
NSMutableArray *words;
NSEnumerator *wordsEnum;
NSString *word;
if (_line == nil)
{
[self cmdQuit: 0];
}
_line = [_line stringByTrimmingSpaces]; // Remove trailing newline
if ([_line length] == 0)
{
return;
}
arp = [[NSAutoreleasePool alloc] init];
/* setup word array */
words = [NSMutableArray arrayWithCapacity: 16];
wordsEnum = [[_line componentsSeparatedByString: @" "] objectEnumerator];
while ((word = [wordsEnum nextObject]) != nil)
{
[words addObject: [word stringByTrimmingSpaces]];
}
/* invoke server */
[self doCommand: words];
[arp release];
}
#define add(C) { if (op - out >= len - 1) { int i = op - out; len += 128; [d setLength: len]; out = [d mutableBytes]; op = &out[i];} *op++ = C; }
- (oneway void) information: (NSString*)s
@ -606,11 +613,15 @@ static BOOL commandIsRepeat (NSString *string)
int len = (ilen + 4) * 2;
char buf[ilen];
const char *ip = buf;
NSMutableData *d = [NSMutableData dataWithCapacity: len];
char *out = [d mutableBytes];
char *op = out;
NSMutableData *d;
char *out;
char *op;
char c;
d = [NSMutableData dataWithCapacity: len];
out = [d mutableBytes];
op = out;
[s getCString: buf];
buf[ilen-1] = '\0';
[d setLength: len];
@ -685,6 +696,36 @@ static BOOL commandIsRepeat (NSString *string)
}
[d setLength: op - out];
[ochan writeData: d];
/* If we are waiting for a regular expression ... check to see if it is
* found and then end with a success status.
*/
if (nil != want)
{
NSRange r;
r = [want rangeOfFirstMatchInString: s
options: 0
range: NSMakeRange(0, [s length])];
if (r.length > 0)
{
[self cmdQuit: 0];
return;
}
}
if (nil != fail)
{
NSRange r;
r = [fail rangeOfFirstMatchInString: s
options: 0
range: NSMakeRange(0, [s length])];
if (r.length > 0)
{
[self cmdQuit: 3]; // Matched failure message ... status 3
return;
}
}
}
#define NUMARGS 1
@ -709,6 +750,8 @@ static BOOL commandIsRepeat (NSString *string)
if (self)
{
NSUserDefaults *defs = [self cmdDefaults];
NSDictionary *env = [[NSProcessInfo processInfo] environment];
NSString *s;
local = [[[NSHost currentHost] name] retain];
name = [defs stringForKey: @"ControlName"];
@ -729,20 +772,156 @@ static BOOL commandIsRepeat (NSString *string)
host = local;
}
[host retain];
/* If the User and Pass arguments are supplied, login directly.
*/
user = [[defs stringForKey: @"User"] retain];
if (nil == user)
{
user = [[env objectForKey: @"ConsoleUser"] retain];
}
pass = [[defs stringForKey: @"Pass"] retain];
if (nil == pass)
{
pass = [[env objectForKey: @"ConsolePass"] retain];
}
rnam = [[NSString stringWithFormat: @"%@:%@", user, local] retain];
data = [[NSMutableData alloc] init];
ichan = [[NSFileHandle fileHandleWithStandardInput] retain];
ochan = [[NSFileHandle fileHandleWithStandardOutput] retain];
if (user && pass)
{
server = [NSConnection rootProxyForConnectionWithRegisteredName: name
host: host
usingNameServer: [NSSocketPortNameServer sharedInstance]];
if (nil == server)
{
if ([host isEqual: @"*"])
{
GSPrintf(stderr, @"Unable to connect to %@ on any host.\n",
name);
}
else
{
GSPrintf(stderr, @"Unable to connect to %@ on %@.\n",
name, host);
}
[self cmdQuit: 1];
DESTROY(self);
}
else
{
NSString *reject;
[server retain];
[server setProtocolForProxy: @protocol(Control)];
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(connectionBecameInvalid:)
name: NSConnectionDidDieNotification
object: (id)[server connectionForProxy]];
[[server connectionForProxy] setDelegate: self];
reject = [server registerConsole: self
name: rnam
pass: pass];
if (reject != nil)
{
GSPrintf(stderr, @"Login rejected for %@ on %@.\n",
name, host);
[self cmdQuit: 1];
DESTROY(self);
}
}
s = [defs stringForKey: @"Line"];
if (0 == [s length])
{
s = [env objectForKey: @"ConsoleLine"];
}
if (nil != self && nil != s)
{
[self doLine: s];
/* Now, we may delay for 'Wait' seconds looking for a response
* containing the 'Want' pattern or the 'Fail' pattern.
*/
s = [defs stringForKey: @"Want"];
if (0 == [s length])
{
s = [env objectForKey: @"ConsoleWant"];
}
if ([s length] > 0)
{
want = [[NSRegularExpression alloc] initWithPattern: s
options: 0
error: 0];
}
s = [defs stringForKey: @"Fail"];
if (0 == [s length])
{
s = [env objectForKey: @"ConsoleFail"];
}
if ([s length] > 0)
{
fail = [[NSRegularExpression alloc] initWithPattern: s
options: 0
error: 0];
}
if (nil != want || nil != fail)
{
NSTimeInterval ti;
s = [defs stringForKey: @"Wait"];
if (0 == [s length])
{
s = [env objectForKey: @"ConsoleWait"];
if (0 == [s length])
{
s = @"5";
}
}
ti = [s floatValue];
timer = [NSTimer scheduledTimerWithTimeInterval: ti
target: self selector: @selector(waitEnded:)
userInfo: nil repeats: NO];
if (YES == [defs boolForKey: @"Quiet"]
|| YES == [[env objectForKey: @"ConsoleQuiet"] boolValue])
{
/* If we don't want any output, return without
* setting the output channel up.
*/
return self;
}
}
else
{
/* No waiting ... quit immediately.
*/
[self cmdQuit: 0];
DESTROY(self);
}
}
}
if (nil != self)
{
data = [[NSMutableData alloc] init];
ichan = [[NSFileHandle fileHandleWithStandardInput] retain];
ochan = [[NSFileHandle fileHandleWithStandardOutput] retain];
#if !defined(HAVE_LIBREADLINE)
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(didRead:)
name: NSFileHandleReadCompletionNotification
object: (id)ichan];
[ochan puts: @"Enter your username: "];
[ichan readInBackgroundAndNotify];
if (nil == user)
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(didRead:)
name: NSFileHandleReadCompletionNotification
object: (id)ichan];
[ochan puts: @"Enter your username: "];
[ichan readInBackgroundAndNotify];
}
#endif
}
}
return self;
}
@ -773,7 +952,12 @@ static BOOL commandIsRepeat (NSString *string)
- (void) timedOut
{
return;
return; // Ignore EcProcess timeouts
}
- (void) waitEnded: (NSTimer*)t
{
[self cmdQuit: 2]; // Timeout waiting for regex has status 2
}
/* readline handling */
@ -788,7 +972,7 @@ readlineReadALine(char *_line)
{
NSString *s = _line ? [[NSString alloc] initWithCString: _line] : nil;
[rlConsole processReadLine: s];
[rlConsole doLine: s];
if (_line != NULL && strlen(_line) > 0)
{

View file

@ -110,6 +110,9 @@ ECCL_AGSDOC_FILES = ECCL.h \
EcLogger.h \
EcProcess.h \
EcUserDefaults.h \
EcCommand.m \
EcControl.m \
EcConsole.m \
ECCL_AGSDOC_FLAGS = \
-MakeFrames YES \