mirror of
https://github.com/gnustep/libs-ec.git
synced 2025-02-20 18:32:09 +00:00
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:
parent
174cfb3841
commit
ba042516d1
5 changed files with 298 additions and 53 deletions
|
@ -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
50
ECCL.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
288
EcConsole.m
288
EcConsole.m
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 \
|
||||
|
|
Loading…
Reference in a new issue