Author: rfm
Date: Tue Mar 18 16:23:33 2014
New Revision: 37751

URL: http://svn.gna.org/viewcvs/gnustep?rev=37751&view=rev
Log:
Add support for non-interactive use of Console

Modified:
    libs/ec/trunk/ChangeLog
    libs/ec/trunk/ECCL.h
    libs/ec/trunk/EcCommand.m
    libs/ec/trunk/EcConsole.m
    libs/ec/trunk/GNUmakefile

Modified: libs/ec/trunk/ChangeLog
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/ec/trunk/ChangeLog?rev=37751&r1=37750&r2=37751&view=diff
==============================================================================
--- libs/ec/trunk/ChangeLog     (original)
+++ libs/ec/trunk/ChangeLog     Tue Mar 18 16:23:33 2014
@@ -1,3 +1,11 @@
+2014-03-18  Richard Frith-Macdonald <[email protected]>
+
+       * ECCL.h:
+       * EcCommand.m:
+       * EcConsole.m:
+       * GNUmakefile:
+       Extend Console tool to support non-interactive use for scripting etc.
+
 2014-02-28  Richard Frith-Macdonald <[email protected]>
 
        * EcControl.m:

Modified: libs/ec/trunk/ECCL.h
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/ec/trunk/ECCL.h?rev=37751&r1=37750&r2=37751&view=diff
==============================================================================
--- libs/ec/trunk/ECCL.h        (original)
+++ libs/ec/trunk/ECCL.h        Tue Mar 18 16:23:33 2014
@@ -37,6 +37,56 @@
           Classes and tools for building and administering 24*7
           server processes for large scale software systems.
         </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>

Modified: libs/ec/trunk/EcCommand.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/ec/trunk/EcCommand.m?rev=37751&r1=37750&r2=37751&view=diff
==============================================================================
--- libs/ec/trunk/EcCommand.m   (original)
+++ libs/ec/trunk/EcCommand.m   Tue Mar 18 16:23:33 2014
@@ -1213,7 +1213,7 @@
   NSRange      r;
   id           o;
   
-  if (nil == config)
+  if (nil == config || 0 == [name length])
     {
       return nil;      // Not available
     }

Modified: libs/ec/trunk/EcConsole.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/ec/trunk/EcConsole.m?rev=37751&r1=37750&r2=37751&view=diff
==============================================================================
--- libs/ec/trunk/EcConsole.m   (original)
+++ libs/ec/trunk/EcConsole.m   Tue Mar 18 16:23:33 2014
@@ -68,6 +68,8 @@
   NSString             *user;
   NSString             *pass;
   NSString             *rnam;
+  NSRegularExpression   *want;
+  NSRegularExpression   *fail;
   id                   server;
   int                  pos;
 }
@@ -82,6 +84,7 @@
 - (void) didWrite: (NSNotification*)notification;
 - (void) doCommand: (NSMutableArray*)words;
 - (void) timedOut;
+- (void) waitEnded: (NSTimer*)t;
 @end
 
 
@@ -96,6 +99,9 @@
 
 - (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 @@
     {
       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 @@
   [host release];
   [name release];
   [local release];
+  [want release];
+  [fail release];
   [data release];
   if (timer)
     {
@@ -200,42 +207,6 @@
   NSLog(@"ERROR: this should not get called w/o readline: %s",
     __PRETTY_FUNCTION__);
 #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)
@@ -598,6 +569,42 @@
     }
 }
 
+- (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,10 +613,14 @@
   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';
@@ -685,6 +696,36 @@
     }
   [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 @@
   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 @@
          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 @@
 
 - (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 @@
 {
   NSString *s = _line ? [[NSString alloc] initWithCString: _line] : nil;
   
-  [rlConsole processReadLine: s];
+  [rlConsole doLine: s];
   
   if (_line != NULL && strlen(_line) > 0)
     {

Modified: libs/ec/trunk/GNUmakefile
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/ec/trunk/GNUmakefile?rev=37751&r1=37750&r2=37751&view=diff
==============================================================================
--- libs/ec/trunk/GNUmakefile   (original)
+++ libs/ec/trunk/GNUmakefile   Tue Mar 18 16:23:33 2014
@@ -110,6 +110,9 @@
        EcLogger.h \
        EcProcess.h \
        EcUserDefaults.h \
+       EcCommand.m \
+       EcControl.m \
+       EcConsole.m \
 
 ECCL_AGSDOC_FLAGS = \
        -MakeFrames YES \


_______________________________________________
Gnustep-cvs mailing list
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs

Reply via email to