libs-ec/EcConsole.m
2024-11-24 16:53:26 +00:00

1256 lines
28 KiB
Objective-C

/** Enterprise Control Configuration and Logging
Copyright (C) 2012 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: Febrary 2010
Originally developed from 1996 to 2012 by Brainstorm, and donated to
the FSF.
This file is part of the GNUstep project.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/Foundation.h>
#import "EcProcess.h"
#import "EcHost.h"
#import "NSFileHandle+Printf.h"
#import "config.h"
#if defined(HAVE_LIBREADLINE)
# include <stdlib.h>
# include <unistd.h>
# include <readline/readline.h>
# include <readline/history.h>
#endif
#if defined(HAVE_READPASSPHRASE_H)
# include <readpassphrase.h>
#elif defined(HAVE_BSD_READPASSPHRASE_H)
# include <bsd/readpassphrase.h>
#endif
static BOOL commandIsRepeat (NSString *string)
{
switch ([string length])
{
case 1: return [string isEqualToString: @"r"];
case 2: return [string isEqualToString: @"re"];
case 3: return [string isEqualToString: @"rep"];
case 4: return [string isEqualToString: @"repe"];
case 5: return [string isEqualToString: @"repea"];
case 6: return [string isEqualToString: @"repeat"];
default: return NO;
}
}
static NSString *originalUserName = nil;
@interface EcConsole : EcProcess <RunLoopEvents, Console>
{
NSFileHandle *ichan;
NSFileHandle *ochan;
NSMutableData *data;
NSTimer *timer;
NSString *local;
NSString *host;
NSString *name;
NSString *user;
NSString *pass;
NSString *rnam;
NSRegularExpression *want;
NSRegularExpression *fail;
id server;
int pos;
BOOL interactive;
}
- (void) connectionBecameInvalid: (NSNotification*)notification;
#if defined(HAVE_LIBREADLINE)
- (void) activateReadline;
- (void) deactivateReadline;
- (void) setupConnection;
#else
- (void) didRead: (NSNotification*)notification;
#endif
- (void) didWrite: (NSNotification*)notification;
- (void) doCommand: (NSMutableArray*)words;
- (void) timedOut;
- (void) waitEnded: (NSTimer*)t;
@end
@implementation EcConsole
- (BOOL) cmdIsClient
{
return NO; // Not a client of the Command server
}
- (oneway 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.
*/
NS_DURING
[ochan puts: @"\nExiting\n"];
NS_HANDLER
NS_ENDHANDLER
#if defined(HAVE_LIBREADLINE)
[self deactivateReadline];
#endif
if (server)
{
id con = [(NSDistantObject*)server connectionForProxy];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSConnectionDidDieNotification
object: con];
NS_DURING
{
[server unregister: self];
}
NS_HANDLER
{
NSLog(@"Exception unregistering from Control: %@", localException);
}
NS_ENDHANDLER
[con invalidate];
DESTROY(server);
}
if (ichan)
{
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSFileHandleReadCompletionNotification
object: (id)ichan];
if ([ichan fileDescriptor] >= 0)
{
[ichan closeFile];
}
[ichan release];
ichan = nil;
}
if (ochan)
{
if ([ochan fileDescriptor] >= 0)
{
[ochan closeFile];
}
[ochan release];
ochan = nil;
}
[super cmdQuit: sig];
}
- (void) connectionBecameInvalid: (NSNotification*)notification
{
id conn = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSConnectionDidDieNotification
object: conn];
if ([conn isKindOfClass: [NSConnection class]])
{
if (server && [(NSDistantObject*)server connectionForProxy] == conn)
{
DESTROY(server);
NSLog(@"Lost connection to Control server.");
[self cmdQuit: 0];
}
}
}
- (void) dealloc
{
[rnam release];
[user release];
[pass release];
[host release];
[name release];
[local release];
[want release];
[fail release];
[data release];
if (timer)
{
[timer invalidate];
timer = nil;
}
[self cmdQuit: 0];
[super dealloc];
}
- (void) receivedEvent: (void *)descriptorData
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
{
#if defined(HAVE_LIBREADLINE)
rl_callback_read_char();
#else
NSLog(@"ERROR: this should not get called w/o readline: %s",
__PRETTY_FUNCTION__);
#endif
}
#if defined(HAVE_LIBREADLINE)
- (char **)complete:(const char *)_text range:(NSRange)_range
{
return NULL; /* no completion yet */
}
#endif
#if !defined(HAVE_LIBREADLINE)
- (void) didRead: (NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
NSData *d;
d = [[userInfo objectForKey: NSFileHandleNotificationDataItem] retain];
if (d == nil || [d length] == 0)
{
if (d != nil)
{
[d release];
}
[self cmdQuit: 0];
}
else
{
char *bytes;
int len;
int eol;
int done = 0;
[data appendData: d];
[d release];
bytes = (char*)[data mutableBytes];
len = [data length];
for (eol = 0; eol < len; eol++)
{
if (bytes[eol] == '\r' || bytes[eol] == '\n')
{
char *word = bytes;
char *end = word;
NSMutableArray *a;
a = [NSMutableArray arrayWithCapacity: 1];
bytes[eol++] = '\0';
while (eol < len && isspace(bytes[eol]))
{
eol++;
}
done = eol;
while (end && *end)
{
word = end;
while (*word && isspace(*word))
{
word++;
}
end = word;
if (*word == '"' || *word == '\'')
{
char term = *word;
char *ptr;
end = ++word;
ptr = end;
while (*end)
{
if (*end == term)
{
end++;
break;
}
if (*end == '\\')
{
end++;
switch (*end)
{
case '\\': *ptr++ = '\\'; break;
case 'b': *ptr++ = '\b'; break;
case 'f': *ptr++ = '\f'; break;
case 'n': *ptr++ = '\n'; break;
case 'r': *ptr++ = '\r'; break;
case 't': *ptr++ = '\t'; break;
case 'x':
{
int val = 0;
if (isxdigit(end[1]))
{
end++;
val <<= 4;
if (islower(*end))
{
val += *end - 'a' + 10;
}
else if (isupper(*end))
{
val += *end - 'A' + 10;
}
else
{
val += *end - '0';
}
}
if (isxdigit(end[1]))
{
end++;
val <<= 4;
if (islower(*end))
{
val += *end - 'a' + 10;
}
else if (isupper(*end))
{
val += *end - 'A' + 10;
}
else
{
val += *end - '0';
}
}
*ptr++ = val;
}
break;
case '0':
{
int val = 0;
if (isdigit(end[1]))
{
end++;
val <<= 3;
val += *end - '0';
}
if (isdigit(end[1]))
{
end++;
val <<= 3;
val += *end - '0';
}
if (isdigit(end[1]))
{
end++;
val <<= 3;
val += *end - '0';
}
*ptr++ = val;
}
break;
default: *ptr++ = *end; break;
}
if (*end)
{
end++;
}
}
else
{
*ptr++ = *end++;
}
}
*ptr = '\0';
}
else
{
while (*end && !isspace(*end))
{
if (isupper(*end))
{
*end = tolower(*end);
}
end++;
}
}
if (*end)
{
*end++ = '\0';
}
else
{
end = 0;
}
if (word && *word)
{
[a addObject: [NSString stringWithCString: word]];
}
}
[self doCommand: a];
if (ichan == nil)
{
return; /* Quit while doing command. */
}
}
}
if (done > 0)
{
memcpy(bytes, &bytes[done], len - done);
[data setLength: len - done];
}
if (YES == interactive)
{
[ichan readInBackgroundAndNotify]; /* Need more data. */
}
}
}
#endif
- (void) didWrite: (NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
NSString *e;
e = [userInfo objectForKey: GSFileHandleNotificationError];
if (e)
{
[self cmdQuit: 0];
}
}
- (void) doCommand: (NSMutableArray*)words
{
NSString *cmd;
static NSArray *pastWords = nil;
if (words == nil || [words count] == 0)
{
return;
}
else
{
cmd = [words objectAtIndex: 0];
}
if ([cmd isEqual: @"quit"] && [words count] == 1)
{
[self cmdQuit: 0];
return;
}
#if !defined(HAVE_LIBREADLINE)
if (user == nil)
{
if ([cmd length] > 0)
{
user = [cmd retain];
[ochan puts: @"Enter your password: "];
}
else
{
[ochan puts: @"Enter your username: "];
}
}
else if (pass == nil)
{
if ([cmd length] > 0)
{
pass = [cmd retain];
server = [NSConnection rootProxyForConnectionWithRegisteredName: name
host: host
usingNameServer: [NSSocketPortNameServer sharedInstance]];
if (server == nil)
{
if ([host isEqual: @"*"])
{
[ochan printf: @"Unable to connect to %@ on any host.\n",
name];
}
else
{
[ochan printf: @"Unable to connect to %@ on %@.\n",
name, host];
}
[self cmdQuit: 10];
return;
}
else
{
NSString *reject;
ASSIGN(rnam,
([NSString stringWithFormat: @"%@:%@", user, local]));
[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)
{
[words removeAllObjects];
[words addObject: @"list"];
[words addObject: @"consoles"];
[server command: [NSPropertyListSerialization
dataFromPropertyList: words
format: NSPropertyListBinaryFormat_v1_0
errorDescription: 0] from: rnam];
}
else
{
if ([reject hasPrefix: @"Already registered "])
{
}
[pass release];
pass = nil;
[user release];
user = nil;
[ochan puts: [NSString stringWithFormat:
@"Connection attempt rejected - %@\n", reject]];
[ochan puts: @"Enter your username: "];
}
}
}
else
{
[ochan puts: @"Enter your password: "];
}
}
else
#endif /* defined(HAVE_LIBREADLINE) */
if (commandIsRepeat(cmd))
{
if (pastWords == nil)
{
[ochan puts: @"No command to repeat.\n"];
}
else
{
[ochan printf: @"Repeating command `%@' -\n",
[pastWords componentsJoinedByString: @" "]];
[server command: [NSPropertyListSerialization
dataFromPropertyList: pastWords
format: NSPropertyListBinaryFormat_v1_0
errorDescription: 0] from: rnam];
}
}
else
{
ASSIGN(pastWords, words);
[server command: [NSPropertyListSerialization
dataFromPropertyList: words
format: NSPropertyListBinaryFormat_v1_0
errorDescription: 0] from: rnam];
}
}
- (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)
{
word = [word stringByTrimmingSpaces];
if ([word length] > 0)
{
[words addObject: word];
}
}
/* 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
{
int ilen = [s cStringLength] + 1;
int len = (ilen + 4) * 2;
char buf[ilen];
const char *ip = buf;
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];
if (pos)
{
add('\n');
pos = 0;
}
while (*ip)
{
switch (*ip)
{
case '\r':
case '\n':
pos = 0;
add(*ip);
break;
case '\t':
if (pos >= 72)
{
pos = 0;
add('\n');
}
else
{
do
{
add(' ');
pos++;
}
while (pos % 8);
}
break;
default:
c = *ip;
if (c < ' ')
{
c = ' ';
}
if (c > 126)
{
c = ' ';
}
if (c == ' ')
{
int l = 0;
while (ip[l] && isspace(ip[l])) l++;
while (ip[l] && !isspace(ip[l])) l++;
l--;
if ((pos + l) >= 80 && l < 80)
{
add('\n');
pos = 0;
}
else
{
add(' ');
pos++;
}
}
else
{
add(c);
pos++;
}
break;
}
ip++;
}
[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
- (id) init
{
NSDictionary *appDefaults;
id objects[NUMARGS];
id keys[NUMARGS];
keys[0] = @"Daemon";
objects[0] = @"NO"; /* Never run as daemon */
appDefaults = [NSDictionary dictionaryWithObjects: objects
forKeys: keys
count: NUMARGS];
return [self initWithDefaults: appDefaults];
}
- (id) initWithDefaults: (NSDictionary*)defs
{
/* When executing a command using sudo, we should use the original username
*/
ASSIGN(originalUserName,
[[[NSProcessInfo processInfo] environment] objectForKey: @"SUDO_USER"]);
if (nil == originalUserName)
{
ASSIGN(originalUserName, NSUserName());
}
self = [super initWithDefaults: defs];
if (self)
{
NSUserDefaults *defs = [self cmdDefaults];
NSDictionary *env = [[NSProcessInfo processInfo] environment];
NSString *s;
interactive = YES;
local = [[[NSHost currentHost] wellKnownName] retain];
name = [defs stringForKey: @"ControlName"];
if (name == nil)
{
name = @"Control";
}
if (nil != (host = [NSHost controlWellKnownName]))
{
host = [[NSHost hostWithWellKnownName: host] name];
}
if (nil == host)
{
host = @"*";
}
if ([host length] == 0)
{
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];
}
ASSIGN(rnam, ([NSString stringWithFormat: @"%@:%@", user, local]));
if (user && pass)
{
BOOL mark = NO;
BOOL prompt = YES;
interactive = NO;
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: 10];
DESTROY(self);
return nil;
}
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: 11];
DESTROY(self);
return nil;
}
}
if ([defs objectForKey: @"Mark"])
{
mark = [defs boolForKey: @"Mark"];
}
else
{
if ([env objectForKey: @"ConsoleMark"])
{
mark = [[env objectForKey: @"ConsoleMark"] boolValue];
}
}
/* Tell the Control server to mark start and end of messages
* if required.
*/
if (mark)
{
[self doCommand: [NSMutableArray arrayWithObjects:
@"silent", @"set", @"display", @"mark", nil]];
}
if ([defs objectForKey: @"Prompt"])
{
prompt = [defs boolForKey: @"Prompt"];
}
else
{
if ([env objectForKey: @"ConsolePrompt"])
{
prompt = [[env objectForKey: @"ConsolePrompt"] boolValue];
}
}
/* Tell the Control server not to prompt if necessary
*/
if (NO == prompt)
{
[self doCommand: [NSMutableArray arrayWithObjects:
@"silent", @"unset", @"display", @"prompt", nil]];
}
if (nil == (s = [defs stringForKey: @"Line"]))
{
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.
* An empty string as a command line argument turns off
* pattern matching specified in the environment.
*/
if (nil == (s = [defs stringForKey: @"Want"]))
{
s = [env objectForKey: @"ConsoleWant"];
}
if ([s length] > 0)
{
want = [[NSRegularExpression alloc] initWithPattern: s
options: 0
error: 0];
}
if (nil == (s = [defs stringForKey: @"Fail"]))
{
s = [env objectForKey: @"ConsoleFail"];
}
if ([s length] > 0)
{
fail = [[NSRegularExpression alloc] initWithPattern: s
options: 0
error: 0];
}
if (nil != want || nil != fail)
{
NSTimeInterval ti;
if (nil == (s = [defs stringForKey: @"Wait"]))
{
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 ([defs objectForKey: @"Quiet"])
{
if ([defs boolForKey: @"Quiet"])
{
/* If we don't want any output, return without
* setting the output channel up.
*/
return self;
}
}
else if ([[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);
return nil;
}
}
}
if (nil != self)
{
data = [[NSMutableData alloc] init];
ichan = [[NSFileHandle fileHandleWithStandardInput] retain];
ochan = [[NSFileHandle fileHandleWithStandardOutput] retain];
#if !defined(HAVE_LIBREADLINE)
if (YES == interactive)
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(didRead:)
name: NSFileHandleReadCompletionNotification
object: (id)ichan];
[ochan puts: @"Enter your username: "];
[ichan readInBackgroundAndNotify];
}
#endif
}
}
return self;
}
- (int) ecRun
{
NSRunLoop *loop;
NSDate *distantFuture;
#if defined(HAVE_LIBREADLINE)
if (YES == interactive)
{
[self setupConnection];
[self activateReadline];
}
#endif
distantFuture = [NSDate distantFuture];
loop = [NSRunLoop currentRunLoop];
while (YES == [loop runMode: NSDefaultRunLoopMode beforeDate: distantFuture])
{
if (0 != [self cmdSignalled])
{
[self cmdQuit: [self cmdSignalled]];
}
}
#if defined(HAVE_LIBREADLINE)
if (YES == interactive)
{
[self deactivateReadline];
}
#endif
return 0;
}
- (void) timedOut
{
return; // Ignore EcProcess timeouts
}
- (void) waitEnded: (NSTimer*)t
{
[self cmdQuit: 2]; // Timeout waiting for regex has status 2
}
/* readline handling */
#if defined(HAVE_LIBREADLINE)
/* readline callbacks have no context, so we need this one ... */
static EcConsole *rlConsole = nil;
static void
readlineReadALine(char *_line)
{
NSString *s = nil;
if (_line)
{
s = [[NSString alloc] initWithCString: _line];
if (strlen(_line) > 0)
{
add_history(_line);
}
free(_line);
}
[rlConsole doLine: s];
DESTROY(s);
}
static char **
consoleCompleter(const char *text, int start, int end)
{
return [rlConsole complete: text range: NSMakeRange(start, end - start)];
}
- (void) activateReadline
{
rlConsole = self;
/* setup readline */
rl_readline_name = "Console";
rl_attempted_completion_function = consoleCompleter;
rl_callback_handler_install("" /* prompt */, readlineReadALine);
atexit(rl_callback_handler_remove);
/* register in runloop */
[[NSRunLoop currentRunLoop] addEvent: (void*)(uintptr_t)0
type: ET_RDESC
watcher: self
forMode: NSDefaultRunLoopMode];
}
- (void) deactivateReadline
{
[[NSRunLoop currentRunLoop] removeEvent: (void*)(uintptr_t)0
type: ET_RDESC
forMode: NSDefaultRunLoopMode
all: YES];
rl_callback_handler_remove();
rlConsole = nil;
}
- (void) setupConnection
{
while (self->server == nil)
{
NSString *u;
NSString *p;
NSString *reject;
char buf[128], *line = buf;
#if !defined(EC_LOGIN_NAME)
#define EC_LOGIN_NAME 0
#endif
#if EC_LOGIN_NAME
if (nil == (u = [[self cmdDefaults] stringForKey: @"EffectiveUser"]))
{
u = originalUserName;
}
/* read username */
if (u)
{
printf("Login (%s): ", [u UTF8String]);
}
else
{
printf("Login: ");
}
fflush(stdout);
line = fgets(buf, sizeof(buf), stdin);
if (0 == line)
{
/* user pressed ctrl-D, just exit */
exit(0);
}
line[strlen(line) - 1] = '\0';
/* If we do not have a system username or if the user entered a value
* we use the username entered (or try again).
*/
if (nil == u || line[0] != '\0')
{
u = [[NSString stringWithCString: line] stringByTrimmingSpaces];
if ([u length] == 0)
{
/* user just pressed enter, retry */
continue;
}
}
#else
u = originalUserName;
#endif
if ([u caseInsensitiveCompare: @"quit"] == NSOrderedSame)
{
[self cmdQuit: 0];
}
p = [NSString stringWithFormat: @"Password (for %@): ", u];
#ifndef HAVE_READPASSPHRASE
/* read password (glibc documentation says not to use getpass?) */
line = getpass([p UTF8String]);
#else
line = readpassphrase([p UTF8String], &buf[0], 128, RPP_ECHO_OFF);
#endif
if (NULL == line)
{
NSLog(@"Could not read password: %s", strerror(errno));
exit(1);
}
p = [[NSString stringWithCString: line] stringByTrimmingSpaces];
if ([p caseInsensitiveCompare: @"quit"] == NSOrderedSame)
{
[self cmdQuit: 0];
}
/* setup connection to server */
self->server = [[NSConnection
rootProxyForConnectionWithRegisteredName: name
host: host
usingNameServer: [NSSocketPortNameServer sharedInstance]] retain];
if (self->server == nil)
{
if ([host isEqual: @"*"])
{
[ochan printf: @"Unable to connect to %@ on any host.\n", name];
}
else
{
[ochan printf: @"Unable to connect to %@ on %@.\n", name, host];
}
[self cmdQuit: 10];
return;
}
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(connectionBecameInvalid:)
name: NSConnectionDidDieNotification
object: (id)[self->server connectionForProxy]];
[server setProtocolForProxy: @protocol(Control)];
[[server connectionForProxy] setDelegate: self];
/* attempt login */
self->user = [u retain];
self->pass = [p retain];
ASSIGN(self->rnam,
([NSString stringWithFormat: @"%@:%@", self->user, self->local]));
reject = [self->server registerConsole: self
name: self->rnam
pass: self->pass];
/* connection failed */
if (reject != nil)
{
[ochan puts: [NSString stringWithFormat:
@"Connection attempt rejected - %@\n", reject]];
[[NSNotificationCenter defaultCenter]
removeObserver: self
name: NSConnectionDidDieNotification
object: (id)[self->server connectionForProxy]];
DESTROY(self->pass);
DESTROY(self->user);
DESTROY(self->rnam);
DESTROY(self->server);
[self cmdQuit: 11];
break;
}
}
}
#endif /* defined(HAVE_LIBREADLINE) */
@end