darling-DSTools/dscl/dscl.m
Andrew Hyatt b1d05a7ba1 Build the one utility we need for now
It's for Homebrew
2017-04-07 18:59:54 -07:00

1476 lines
38 KiB
Objective-C

/*
* Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*!
* @header dscl
* directory service command-line interface
* Main command processor derived from nicl source.
*/
#import <Foundation/Foundation.h>
#import <DirectoryService/DirectoryService.h>
#import <stdio.h>
#import <unistd.h>
#import <stdlib.h>
#import <sys/types.h>
#import <sys/ioctl.h>
#import <termios.h>
#import <histedit.h>
#import "PathManager.h"
#import "DSoStatus.h"
#import "DSoException.h"
#import "DSCLCommandHistory.h"
#import "NSStringEscPath.h"
#import "dstools_version.h"
#import <sysexits.h>
#import <sys/sysctl.h>
#import "PlugInManager.h" // ATM - support for plugins
#define streq(A,B) (strcmp(A,B) == 0)
#define forever for(;;)
static char myname[256];
PathManager *engine = nil;
BOOL gHACK = NO;
BOOL gURLEncode = NO;
static int interactive = 0;
#define INPUT_LENGTH 4096
#define PROMPT_NONE 0
#define PROMPT_PLAIN 1
#define PROMPT_DS 2
#define ATTR_CREATE 0
#define ATTR_APPEND 1
#define ATTR_MERGE 2
#define ATTR_DELETE 3
#define ATTR_CHANGE 4
#define ATTR_CHANGE_INDEX 5
//not all of these are actually used but are placeholder carryovers from nicl
static int minargs[] =
{
-1, // noop
1, // create
1, // delete
3, // rename
1, // read
1, // list
3, // append
3, // merge
4, // insert
2, // move
2, // copy
3, // search
1, // path
0, // pushd
0, // popd
2, // parent
1, // cd
0, // pwd
0, // version history
0, // stats
0, // domain name
0, // rparent
0, // resync
1, // authenticate
0, // refs
2, // setrdn
4, // change
4, // changei
1, // authonly
1, // readall
2, // diff
3, // readpl
4, // createpl
3, // deletepl
4, // readpli
5, // createpli
4 // deletepli
};
#define OP_NOOP 0
#define OP_CREATE 1
#define OP_DELETE 2
#define OP_READ 4
#define OP_LIST 5
#define OP_APPEND 6
#define OP_MERGE 7
#define OP_SEARCH 11
#define OP_PUSHD 13
#define OP_POPD 14
#define OP_CD 16
#define OP_PASSWD 18
#define OP_AUTH 23
#define OP_CHANGE 26
#define OP_CHANGE_INDEX 27
#define OP_AUTH_ONLY 28
#define OP_READALL 29
#define OP_DIFF 30
#define OP_READPL 31
#define OP_CREATEPL 32
#define OP_DELETEPL 33
#define OP_READPL_INDEX 34
#define OP_CREATEPL_INDEX 35
#define OP_DELETEPL_INDEX 36
void usage()
{
fprintf(stderr, "dscl (v%s)\n", TOOLS_VERSION);
fprintf(stderr, "usage: %s [options] [<datasource> [<command>]]\n", myname);
fprintf(stderr, "datasource:\n");
fprintf(stderr, " localhost (default) or\n");
fprintf(stderr, " localonly (activates a DirectoryService daemon process \n");
fprintf(stderr, " with Local node only - daemon quits after use \n");
fprintf(stderr, " <hostname> (requires DS proxy support, >= DS-158) or\n");
fprintf(stderr, " <nodename> (Directory Service style node name) or\n");
fprintf(stderr, " <domainname> (NetInfo style domain name)\n");
fprintf(stderr, "options:\n");
fprintf(stderr, " -u <user> authenticate as user (required when using DS Proxy)\n");
fprintf(stderr, " -P <password> authentication password\n");
fprintf(stderr, " -p prompt for password\n");
fprintf(stderr, " -f <filepath> targeted file path for DS daemon running in localonly mode\n");
fprintf(stderr, " (example: /Volumes/Build100/var/db/dslocal/nodes/Default) \n");
fprintf(stderr, " (NOTE: Nodename to use is fixed at /Local/Target) \n");
fprintf(stderr, " -raw don't strip off prefix from DS constants\n");
fprintf(stderr, " -plist print out record(s) or attribute(s) in XML plist format\n");
fprintf(stderr, " -url print record attribute values in URL-style encoding\n");
fprintf(stderr, " -q quiet - no interactive prompt\n");
fprintf(stderr, "commands:\n");
fprintf(stderr, " -read <path> [<key>...]\n");
fprintf(stderr, " -readall <path> [<key>...]\n");
fprintf(stderr, " -readpl <path> <key> <plist path>\n");
fprintf(stderr, " -readpli <path> <key> <value index> <plist path>\n");
fprintf(stderr, " -create <record path> [<key> [<val>...]]\n");
fprintf(stderr, " -createpl <record path> <key> <plist path> <val1> [<val2>...]\n");
fprintf(stderr, " -createpli <record path> <key> <value index> <plist path> <val1> [<val2>...]\n");
fprintf(stderr, " -delete <path> [<key> [<val>...]]\n");
fprintf(stderr, " -deletepl <record path> <key> <plist path> [<val>...]\n");
fprintf(stderr, " -deletepli <record path> <key> <value index> <plist path> [<val>...]\n");
fprintf(stderr, " -list <path> [<key>]\n");
fprintf(stderr, " -append <record path> <key> <val>...\n");
fprintf(stderr, " -merge <record path> <key> <val>...\n");
fprintf(stderr, " -change <record path> <key> <old value> <new value>\n");
fprintf(stderr, " -changei <record path> <key> <value index> <new value>\n");
fprintf(stderr, " -diff <first path> <second path>\n");
fprintf(stderr, " -search <path> <key> <val>\n");
fprintf(stderr, " -auth [<user> [<password>]]\n");
fprintf(stderr, " -authonly [<user> [<password>]]\n");
fprintf(stderr, " -passwd <user path> [<new password> | <old password> <new password>]\n");
// ATM - give the plugins a chance to display their own help
dscl_PlugInShowUsage(stderr);
}
tDirStatus
dscl_attribute(u_int32_t dsid, int flag, int argc, char *argv[])
{
// <path> [<key> [<val>...]]
tDirStatus status = eDSNoErr;
u_int32_t i;
NSString *path = nil;
NSString *key = nil;
NSMutableArray *values = nil;
if (argc == 0) return eDSNoErr;
path = [[NSString alloc] initWithUTF8String:argv[0]];
argc--;
argv++;
if (argc > 0 && argv[0] != NULL)
{
// We have a key to use:
key = [[NSString alloc] initWithUTF8String:argv[0]];
argc--;
argv++;
if (argc > 0 && argv[0] != NULL)
{
// we have values
NSString *val;
values = [[NSMutableArray alloc] initWithCapacity:argc];
for (i = 0; i < argc; i++)
{
val = [[NSString alloc] initWithUTF8String:argv[i]];
[values addObject:val];
[val release];
}
}
}
NS_DURING
switch (flag)
{
case ATTR_CREATE:
status = [engine createRecord:path key:key values:values];
break;
case ATTR_APPEND:
status = [engine appendToRecord:path key:key values:values];
break;
case ATTR_MERGE:
status = [engine mergeToRecord:path key:key values:values];
break;
case ATTR_DELETE:
status = [engine deleteInRecord:path key:key values:values];
break;
case ATTR_CHANGE:
status = [engine changeInRecord:path key:key values:values];
break;
case ATTR_CHANGE_INDEX:
status = [engine changeInRecordByIndex:path key:key values:values];
break;
}
NS_HANDLER
[path release];
[key release];
[values release];
[localException raise];
NS_ENDHANDLER
if (status != eDSNoErr)
fprintf(stderr, "<main> attribute status: %s\n", [[DSoStatus sharedInstance] cStringForStatus:status]);
[path release];
[key release];
[values release];
return status;
}
tDirStatus
dscl_create(u_int32_t dsid, int argc, char *argv[])
{
return dscl_attribute(dsid, ATTR_CREATE, argc, argv);
}
tDirStatus
dscl_append(u_int32_t dsid, int argc, char *argv[])
{
return dscl_attribute(dsid, ATTR_APPEND, argc, argv);
}
tDirStatus
dscl_merge(u_int32_t dsid, int argc, char *argv[])
{
return dscl_attribute(dsid, ATTR_MERGE, argc, argv);
}
tDirStatus
dscl_change(u_int32_t dsid, int argc, char *argv[])
{
return dscl_attribute(dsid, ATTR_CHANGE, argc, argv);
}
tDirStatus
dscl_change_index(u_int32_t dsid, int argc, char *argv[])
{
return dscl_attribute(dsid, ATTR_CHANGE_INDEX, argc, argv);
}
tDirStatus
dscl_delete(u_int32_t dsid, int argc, char *argv[])
{
// <path> [<key> [<val>...]]
tDirStatus status = eDSNoErr;
if (argc == 0)
{
fprintf(stderr, "<main> delete requires a path\n");
}
if (argc == 1)
{
// We are deleting a whole record:
status = [engine deleteRecord:[NSString stringWithUTF8String:argv[0]]];
if (status != eDSNoErr)
fprintf(stderr, "<main> delete status: %s\n", [[DSoStatus sharedInstance] cStringForStatus:status]);
}
else
{
// we are just deleting keys and/or values from a record.
status = dscl_attribute(dsid, ATTR_DELETE, argc, argv);
}
return status;
}
tDirStatus
dscl_diff(u_int32_t dsid, int argc, char *argv[])
{
// <path1> <path2> [<key> ...]
NSString *path1 = nil;
NSString *path2 = nil;
NSMutableArray *keys = nil;
path1 = [[NSString alloc] initWithUTF8String:argv[0]];
path2 = [[NSString alloc] initWithUTF8String:argv[1
]];
argc -= 2;
argv += 2;
keys = [[NSMutableArray alloc] initWithCapacity:argc];
while (argc > 0)
{
[keys addObject:[NSString stringWithUTF8String:argv[0]]];
argc--;
argv++;
}
NS_DURING
[engine diff:path1 otherPath:path2 keys:keys];
NS_HANDLER
[keys release];
[path1 release];
[path2 release];
[localException raise];
NS_ENDHANDLER
[keys release];
[path1 release];
[path2 release];
return eDSNoErr;
}
tDirStatus
dscl_read(u_int32_t dsid, int argc, char *argv[], BOOL all)
{
// <path> [<key> ...]
NSString *path = nil;
NSMutableArray *keys = nil;
if (argc == 0)
{
if (all)
return [engine readAll:nil keys:nil];
else
return [engine read:nil keys:nil];
}
path = [[NSString alloc] initWithUTF8String:argv[0]];
argc--;
argv++;
keys = [[NSMutableArray alloc] initWithCapacity:argc];
while (argc > 0)
{
[keys addObject:[NSString stringWithUTF8String:argv[0]]];
argc--;
argv++;
}
@try {
if (all)
return [engine readAll:path keys:keys];
else
return [engine read:path keys:keys];
}
@finally {
[keys release];
[path release];
}
return eDSNoErr;
}
tDirStatus
dscl_readpl(u_int32_t dsid, int argc, char *argv[])
{
// <path> <key> <plist path>
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
plistPath = [[NSString alloc] initWithUTF8String:argv[2]];
NS_DURING
NS_VALUERETURN(([engine read:path key:key plistPath:plistPath]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_readpli(u_int32_t dsid, int argc, char *argv[])
{
// <path> <key> <value index> <plist path>
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
NSString *sindex = nil;
int index = 0;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
sindex = [[NSString alloc] initWithUTF8String:argv[2]];
plistPath = [[NSString alloc] initWithUTF8String:argv[3]];
index = [sindex intValue];
NS_DURING
NS_VALUERETURN(([engine read:path key:key atIndex:index plistPath:plistPath]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_createpl(u_int32_t dsid, int argc, char *argv[])
{
// <record path> <key> <plist path> <val1> [<val2>...]
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
NSMutableArray *values = nil;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
plistPath = [[NSString alloc] initWithUTF8String:argv[2]];
unsigned int c;
values = [[NSMutableArray alloc] init];
for(c = 3; c < argc; c++)
{
[values addObject:[NSString stringWithUTF8String:argv[c]]];
}
NS_DURING
NS_VALUERETURN(([engine create:path key:key plistPath:plistPath values:values]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_createpli(u_int32_t dsid, int argc, char *argv[])
{
// <record path> <key> <plist path> <val1> [<val2>...]
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
NSMutableArray *values = nil;
NSString *sindex = nil;
int index = 0;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
sindex = [[NSString alloc] initWithUTF8String:argv[2]];
plistPath = [[NSString alloc] initWithUTF8String:argv[3]];
index = [sindex intValue];
unsigned int c;
values = [[NSMutableArray alloc] init];
for(c = 4; c < argc; c++)
{
[values addObject:[NSString stringWithUTF8String:argv[c]]];
}
NS_DURING
NS_VALUERETURN(([engine create:path key:key atIndex:index plistPath:plistPath values:values]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_deletepl(u_int32_t dsid, int argc, char *argv[])
{
// <path> <key> <plist path> [<val>...]
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
NSMutableArray *values = nil;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
plistPath = [[NSString alloc] initWithUTF8String:argv[2]];
unsigned int c;
values = [[NSMutableArray alloc] init];
for(c = 3; c < argc; c++)
{
[values addObject:[NSString stringWithUTF8String:argv[c]]];
}
NS_DURING
NS_VALUERETURN(([engine delete:path key:key plistPath:plistPath values:values]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_deletepli(u_int32_t dsid, int argc, char *argv[])
{
// <path> <key> <value index> <plist path> [<val>...]
NSString *path = nil;
NSString *key = nil;
NSString *plistPath = nil;
NSMutableArray *values = nil;
NSString *sindex = nil;
int index = 0;
path = [[NSString alloc] initWithUTF8String:argv[0]];
key = [[NSString alloc] initWithUTF8String:argv[1]];
sindex = [[NSString alloc] initWithUTF8String:argv[2]];
plistPath = [[NSString alloc] initWithUTF8String:argv[3]];
index = [sindex intValue];
unsigned int c;
values = [[NSMutableArray alloc] init];
for(c = 4; c < argc; c++)
{
[values addObject:[NSString stringWithUTF8String:argv[c]]];
}
NS_DURING
NS_VALUERETURN(([engine delete:path key:key atIndex:index plistPath:plistPath values:values]), tDirStatus);
NS_HANDLER
[path release];
[localException raise];
NS_ENDHANDLER
[path release];
return eDSNoErr;
}
tDirStatus
dscl_list(u_int32_t dsid, int argc, char *argv[])
{
// <path> [<key>]
NSString *path = nil;
NSString *key = nil;
if (argc > 0)
{
if (argv[0] != NULL)
path = [[NSString alloc] initWithUTF8String:argv[0]];
if (argc > 1 && argv[1] != NULL)
key = [[NSString alloc] initWithUTF8String:argv[1]];
}
[engine list:path key:key];
[path release];
[key release];
return eDSNoErr;
}
tDirStatus
dscl_cd(u_int32_t dsid, int argc, char *argv[])
{
if (argc > 0 && argv[0] != NULL)
[engine cd:[NSString stringWithUTF8String:argv[0]]];
return eDSNoErr;
}
tDirStatus
dscl_search(u_int32_t dsid, int argc, char *argv[])
{
// <path> <key> <val> [<match type>]
tDirStatus status = eDSNoErr;
NSString *path = [[NSString alloc] initWithUTF8String:argv[0]];
NSString *key = [[NSString alloc] initWithUTF8String:argv[1]];
NSString *valuePattern = [[NSString alloc] initWithUTF8String:argv[2]];
NSString *matchType = nil;
if (argc > 3)
matchType = [[NSString alloc] initWithUTF8String:argv[3]];
NS_DURING
[engine searchInPath:path forKey:key withValue:valuePattern matchType:matchType];
NS_HANDLER
[path release];
[key release];
[valuePattern release];
[matchType release];
[localException raise];
NS_ENDHANDLER
[path release];
[key release];
[valuePattern release];
[matchType release];
return status;
}
tDirStatus
dscl_pushd(u_int32_t dsid, int argc, char *argv[])
{
// [<path>]
tDirStatus status = eDSNoErr;
NSString *path = nil;
if (argc == 0)
[engine pushd:nil];
else
{
path = [[NSString alloc] initWithUTF8String:argv[0]];
[engine pushd:path];
[path release];
}
return status;
}
tDirStatus
dscl_popd(u_int32_t dsid, int argc, char *argv[])
{
// <path> <dsid>
tDirStatus status = eDSNoErr;
[engine popd];
return status;
}
tDirStatus
dscl_set_password(u_int32_t dsid, int argc, char *argv[])
{
NSString *path;
id passwordItems = nil;
tDirStatus status = eDSNoErr;
if (argc < 1 || argc > 3)
{
return eDSAuthFailed;
}
else
{
path = [NSString stringWithUTF8String:argv[0]];
if (path == nil) return eDSAuthFailed;
argc--;
argv++;
}
if (argc > 0)
{
passwordItems = [NSMutableArray arrayWithCapacity:argc];
for (;argc > 0; argc--, argv++) {
[passwordItems addObject:[NSString stringWithUTF8String:argv[0]]];
bzero( argv[0], strlen(argv[0]) );
}
}
else
{
passwordItems = [NSArray arrayWithObject:[NSString stringWithUTF8String:getpass("New Password: ")]];
}
status = [engine setPasswordForUser:path withParams:passwordItems];
return status;
}
tDirStatus
dscl_authenticate(int argc, char *argv[], BOOL inAuthOnly)
{
NSString *u = nil;
NSString *p = nil;
tDirStatus status = eDSNoErr;
if (argc == 0)
{
u = @"root";
}
else
{
u = [NSString stringWithUTF8String:argv[0]];
if (u == nil) return eDSAuthFailed;
argc--;
argv++;
}
if (argc > 0)
{
p = [NSString stringWithUTF8String:argv[0]];
}
else
{
p = [NSString stringWithUTF8String:getpass("Password: ")];
}
if (p == NULL)
{
return eDSAuthFailed;
}
status = [engine authenticateUser:u password:p authOnly:inAuthOnly];
return status;
}
int
dscl_cmd(int cc, char *cv[])
{
tDirStatus status = eDSNoErr;
int op = OP_NOOP;
int dsid = 0; //not used but left for future?
int do_path = 1;
char *cmd = nil;
cmd = cv[0];
if (cmd[0] == '-') cmd++;
if (streq(cmd, "help"))
{
usage();
return eDSNoErr;
}
else if (streq(cmd, "create")) op = OP_CREATE;
else if (streq(cmd, "createprop")) op = OP_CREATE;
else if (streq(cmd, "createpl")) op = OP_CREATEPL;
else if (streq(cmd, "createpli")) op = OP_CREATEPL_INDEX;
else if (streq(cmd, "delete")) op = OP_DELETE;
else if (streq(cmd, "destroy")) op = OP_DELETE;
else if (streq(cmd, "destroyprop")) op = OP_DELETE;
else if (streq(cmd, "deletepl")) op = OP_DELETEPL;
else if (streq(cmd, "deletepli")) op = OP_DELETEPL_INDEX;
else if (streq(cmd, "read")) op = OP_READ;
else if (streq(cmd, "readall")) op = OP_READALL;
else if (streq(cmd, "readpl")) op = OP_READPL;
else if (streq(cmd, "readpli")) op = OP_READPL_INDEX;
else if (streq(cmd, "diff")) op = OP_DIFF;
else if (streq(cmd, "list")) op = OP_LIST;
else if (streq(cmd, "append")) op = OP_APPEND;
else if (streq(cmd, "merge")) op = OP_MERGE;
else if (streq(cmd, "search")) op = OP_SEARCH;
else if (streq(cmd, "pushd")) op = OP_PUSHD;
else if (streq(cmd, "pd")) op = OP_PUSHD;
else if (streq(cmd, "popd")) op = OP_POPD;
else if (streq(cmd, "cd")) op = OP_CD;
else if (streq(cmd, "mk")) op = OP_CREATE;
else if (streq(cmd, "rm")) op = OP_DELETE;
else if (streq(cmd, "cat")) op = OP_READ;
else if (streq(cmd, ".")) op = OP_READ;
else if (streq(cmd, "ls")) op = OP_LIST;
else if (streq(cmd, "passwd")) op = OP_PASSWD;
else if (streq(cmd, "auth") || streq(cmd, "su"))
{
op = OP_AUTH;
do_path = 0;
}
else if (streq(cmd, "authonly"))
{
op = OP_AUTH_ONLY;
do_path = 0;
}
else if (streq(cmd, "change")) op = OP_CHANGE;
else if (streq(cmd, "changei")) op = OP_CHANGE_INDEX;
// ATM - the command is not a built-in command; see if any of the plugin(s) will claim it
else if (dscl_PlugInDispatch(cc, cv, interactive, dsid, engine, &status))
{
// Exit codes seem to get truncated to 0...255 range so if we get something outside this range, convert to something standardized
if (status < 0 || status > EX__MAX /* 78 */)
status = EX_OSERR;
return status;
}
else
{
usage();
return eDSNoErr;
}
cc--;
cv++;
if ((interactive == 1) && (cc == 0) && (minargs[op] == 1))
{
// default path arg to current directory
}
else if (cc < minargs[op])
{
fprintf(stderr, "Too few parameters for %s operation\n", cmd);
usage();
return EX_USAGE;
}
status = eDSNoErr;
NS_DURING
switch (op)
{
case OP_NOOP:
status = eDSNoErr;
break;
case OP_CREATE:
status = dscl_create(dsid, cc, cv);
break;
case OP_DELETE:
status = dscl_delete(dsid, cc, cv);
break;
case OP_READ:
status = dscl_read(dsid, cc, cv, NO);
break;
case OP_READALL:
status = dscl_read(dsid, cc, cv, YES);
break;
case OP_READPL:
status = dscl_readpl(dsid, cc, cv);
break;
case OP_READPL_INDEX:
status = dscl_readpli(dsid, cc, cv);
break;
case OP_CREATEPL:
status = dscl_createpl(dsid, cc, cv);
break;
case OP_CREATEPL_INDEX:
status = dscl_createpli(dsid, cc, cv);
break;
case OP_DELETEPL:
status = dscl_deletepl(dsid, cc, cv);
break;
case OP_DELETEPL_INDEX:
status = dscl_deletepli(dsid, cc, cv);
break;
case OP_DIFF:
status = dscl_diff(dsid, cc, cv);
break;
case OP_LIST:
status = dscl_list(dsid, cc, cv);
break;
case OP_APPEND:
status = dscl_append(dsid, cc, cv);
break;
case OP_MERGE:
status = dscl_merge(dsid, cc, cv);
break;
case OP_SEARCH:
status = dscl_search(dsid, cc, cv);
break;
case OP_PUSHD:
status = dscl_pushd(dsid, cc, cv);
break;
case OP_POPD:
status = dscl_popd(dsid, cc, cv);
break;
case OP_CD:
status = dscl_cd(dsid, cc, cv);
break;
case OP_PASSWD:
status = dscl_set_password(dsid, cc, cv);
break;
case OP_AUTH_ONLY:
status = dscl_authenticate(cc, cv, YES);
break;
case OP_AUTH:
status = dscl_authenticate(cc, cv, NO);
break;
case OP_CHANGE:
status = dscl_change(dsid, cc, cv);
break;
case OP_CHANGE_INDEX:
status = dscl_change_index(dsid, cc, cv);
break;
}
NS_HANDLER
[engine restoreStack]; // In case the error happened before the stack was restored.
if ([[localException name] isEqualToString:@"DSCL"])
{
printf("%s: %s\n", cmd, [[localException reason] UTF8String]);
status = eDSUnknownNodeName;
}
else if ([localException isKindOfClass:[DSoException class]])
{
printf("%s: DS error: %s\n", cmd, [(DSoException*)localException statusCString]);
status = [(DSoException*)localException status];
}
else
[localException raise];
NS_ENDHANDLER
if (status != eDSNoErr)
fprintf(stderr, "<dscl_cmd> DS Error: %d (%s)\n", status, [[DSoStatus sharedInstance] cStringForStatus:status]);
// Reverse the sign in order to exit with a positive value.
// Certain external tool expect this to be positive.
return -status;
}
char *
dscl_get_string(char **s)
{
char *p = nil;
char *x = nil;
int i = 0;
int quote = 0;
int esc = 0;
if (*s == NULL) return NULL;
if (**s == '\0') return NULL;
// Skip leading white space
while ((**s == ' ') || (**s == '\t')) *s += 1;
if (**s == '\0') return NULL;
x = *s;
if (*x == '\"' || *x == '\'')
{
quote = 1;
*s += 1;
x = *s;
}
forever
{
if (x[i] == '\0') break;
if ((quote == 1) && ((x[i] == '\"') || (x[i] == '\''))) break;
if ((quote == 0) && (x[i] == ' ' )) break;
if ((quote == 0) && (x[i] == '\t')) break;
if (x[i] == '\\')
{
if (esc == 0) esc = i;
i++;
if (x[i] == '\0') break;
}
i++;
}
p = malloc(i+1);
memmove(p, x, i);
p[i] = 0;
if (quote == 1) i++;
*s += i;
while (esc != 0)
{
i = esc + 1;
if (p[i] == '\\') i++;
esc = 0;
for (; p[i] != '\0'; i++)
{
p[i-1] = p[i];
if ((p[i] == '\\') && (esc == 0)) esc = i - 1;
}
p[i - 1] = '\0';
}
return p;
}
int
dscl_tabcomplete(EditLine *e, int ch)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *possibleCompletions = nil;
int cntLimit = 0;
const LineInfo *li = el_line(e);
// Remove any pre-existing trailing slash:
if (*(li->lastchar) == '/')
{
el_deletestr(e, 1);
}
NSString *inputLine = [[NSString alloc] initWithBytes:li->buffer length:(li->lastchar - li->buffer) encoding:NSUTF8StringEncoding];
NSRange cmdSeparator = [inputLine rangeOfString:@" "]; // find the command-line separator
if( cmdSeparator.location != NSNotFound )
{
NSMutableString *completePath = [NSMutableString stringWithString:[inputLine substringFromIndex: (cmdSeparator.location + 1)]];
NSMutableArray *lineComponents = [NSMutableArray arrayWithArray: [completePath unescapedPathComponents]];
NSString *partialPath = [lineComponents lastObject];
possibleCompletions = [engine getPossibleCompletionsFor: completePath];
if (possibleCompletions != nil)
{
cntLimit = [possibleCompletions count];
if (cntLimit > 0)
{
// For now, newLine is just the current completion.
NSEnumerator *compEnum = [possibleCompletions objectEnumerator];
NSString *partialMatch = [compEnum nextObject];
NSString *currentItem;
while( currentItem = [compEnum nextObject] )
{
NSString *commonMatch = [currentItem commonPrefixWithString:partialMatch options:NSCaseInsensitiveSearch];
if( [commonMatch length] < [partialMatch length] )
partialMatch = commonMatch;
}
// Remove the partial item from the input line to
// make room for the complete value.
el_deletestr(e, [[partialPath escapedString] length]);
// If there were multiple completion possibilities, list
// them out for the user.
if (cntLimit > 1)
{
putchar('\n');
compEnum = [possibleCompletions objectEnumerator];
while( currentItem = [compEnum nextObject] ) {
printf("%s \t", [[currentItem escapedString] UTF8String]);
}
putchar('\n');
// Insert the partial completion into the input line
el_insertstr(e, [[partialMatch escapedString] UTF8String]);
}
else
{
// Otherwise add a trailing slash and do in-line
// completion since this is the only completion.
el_insertstr(e, [[[partialMatch escapedString] stringByAppendingString:@"/"] UTF8String]);
}
}
else
putchar(7);
}
else
putchar(7);
}
[inputLine release];
[pool release];
switch (cntLimit)
{
case 0:
return CC_ARGHACK;
break;
case 1:
return CC_REFRESH;
break;
default:
return CC_REDISPLAY;
break;
}
}
static char *
dscl_input_line(FILE *in, EditLine *el, History *h)
{
HistEvent hev;
const char *eline = NULL;
char *out = NULL;
char fline[INPUT_LENGTH];
int count = 0;
int len = 0;
if (el == NULL)
{
count = fscanf(in, "%[^\n]%*c", fline);
if (count < 0) return NULL;
if (count == 0)
{
fscanf(in, "%*c");
out = calloc(1, 1);
return out;
}
len = strlen(fline);
out = malloc(len + 1);
memmove(out, fline, len);
out[len] = '\0';
return out;
}
eline = el_gets(el, &count);
if (eline == NULL) return NULL;
if (count <= 0) return NULL;
// Strip trailing '/' if it's not by itself.
if (count >= 3 && eline[count-2] == '/' && eline[count-3] != ' ')
len = count - 2;
else
len = count - 1;
out = malloc(len + 1);
memmove(out, eline, len);
out[len] = '\0';
if (len > 0) history(h, &hev, H_ENTER, out);
return out;
}
char *
dscl_prompt(EditLine *el)
{
NSString *P = [NSString stringWithFormat:@"\e[1m%@\e[0m > ", [[engine cwd] unescapedString]];
return (char*)[P UTF8String];
}
int
dscl_interactive(FILE *in, int pmt)
{
char *s = nil;
char *p = nil;
char **iargv = NULL;
char *line = NULL;
int i = 0;
int iargc = 0;
int quote = 0;
int esc = 0;
EditLine *el = NULL;
History *h = NULL;
HistEvent hev;
NSAutoreleasePool *foreverPool = nil;
interactive = 1;
if (pmt != PROMPT_NONE)
{
el = el_init("dscl", in, stdout, stderr);
h = history_init();
el_set(el, EL_HIST, history, h);
el_set(el, EL_PROMPT, dscl_prompt);
el_set(el, EL_EDITOR, "emacs");
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_EDITMODE, 1);
// Tell the command line editor library to use
// the tab completion routine when tab is pressed.
el_set(el, EL_ADDFN, "tabcomplete", "Tab completion", dscl_tabcomplete);
el_set(el, EL_BIND, "^I", "tabcomplete", NULL);
history(h, &hev, H_SETSIZE, 100000);
}
forever
{
foreverPool = [[NSAutoreleasePool alloc] init];
line = dscl_input_line(in, el, h);
if (line == NULL) break;
if (line[0] == '\0')
{
free(line);
continue;
}
if (streq(line, "exit")) break;
if (streq(line, "quit")) break;
if (streq(line, "q")) break;
p = line;
iargc = 0;
quote = 0;
esc = 0;
forever
{
s = dscl_get_string(&p);
if (s == NULL) break;
if (iargc == 0)
iargv = (char **)malloc(sizeof(char *));
else
iargv = (char **)realloc(iargv, (iargc + 1) * sizeof(char *));
iargv[iargc++] = s;
}
dscl_cmd(iargc, iargv);
for (i = 0; i < iargc; i++) free(iargv[i]);
free(iargv);
free(line);
[foreverPool release];
}
return eDSNoErr;
}
uint32_t isSingleUserMode( void )
{
uint32_t sb = 0;
size_t sbsz = sizeof(sb);
if ( sysctlbyname("kern.singleuser", &sb, &sbsz, NULL, 0) == -1 )
return NO;
return sb;
}
int
main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i = 0;
int opt_tag = 0;
int opt_promptpw = 0;
int opt_user = 0;
int opt_password = 0;
int opt_filePath = 0;
int prompt = PROMPT_DS;
char *slash = nil;
char *dataSource = nil;
tDirStatus status = eDSNoErr;
NSString *auth_user = nil;
NSString *auth_password = nil;
NSString *filePath = nil;
extern BOOL gRawMode;
extern BOOL gPlistMode;
@try {
slash = rindex(argv[0], '/');
if (slash == NULL) strcpy(myname, argv[0]);
else strcpy(myname, slash+1);
if ( argc == 2 && strcmp(argv[1], "-appleversion") == 0 )
dsToolAppleVersionExit( myname );
interactive = 0;
if ( isSingleUserMode() ) {
NSArray *args = [NSArray arrayWithObjects: @"load",
@"/System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist",
nil];
NSTask *task = [NSTask launchedTaskWithLaunchPath: @"/bin/launchctl" arguments: args];
[task waitUntilExit];
}
// Parse program options
for (i = 1; i < argc; i++)
{
if (streq(argv[i], "-H")) gHACK = YES;
else if (streq(argv[i], "-q")) prompt = PROMPT_NONE;
else if (streq(argv[i], "-t")) opt_tag = 1;
else if (streq(argv[i], "-raw")) gRawMode = 1;
else if (streq(argv[i], "-plist")) gPlistMode = 1;
else if (streq(argv[i], "-p")) opt_promptpw = 1;
else if (streq(argv[i], "-url")) gURLEncode = YES;
else if (streq(argv[i], "-P"))
{
i++;
opt_password = i;
}
else if (streq(argv[i], "-u"))
{
i++;
opt_user = i;
}
else if (streq(argv[i], "-f"))
{
i++;
opt_filePath = i;
}
else break;
}
if (i == argc)
{
printf("Entering interactive mode... (type \"help\" for commands)\n");
if ( isSingleUserMode() ) {
dataSource = "localonly";
}
else {
dataSource = "localhost";
}
}
else
{
dataSource = argv[i];
}
if (opt_user) opt_promptpw = 1;
if (opt_password) opt_promptpw = 0;
if ((opt_user == 0) && ((opt_password == 1) || (opt_promptpw == 1)))
{
auth_user = @"root";
}
if (opt_filePath != 0)
filePath = [NSString stringWithUTF8String:argv[opt_filePath]];
if (opt_user != 0)
auth_user = [NSString stringWithUTF8String:argv[opt_user]];
if (opt_password != 0)
{
auth_password = [NSString stringWithUTF8String:argv[opt_password]];
// after retrieving the password from the command line, blank it out from the ps listing
bzero( argv[opt_password], strlen(argv[opt_password]) );
}
else if (opt_promptpw == 1)
auth_password = [NSString stringWithUTF8String:getpass("Password: ")];
if (filePath != NULL)
{
// The data source is the raw local file, open the local only DS service.
engine = [[PathManager alloc] initWithLocalPath:filePath];
}
else if (dataSource[0] == '/')
{
// The data source begins with a "/", it's a node,
// open the node via the local DS service.
// (Authenticate to the node if necessary.)
if (auth_user && auth_password)
engine = [[PathManager alloc] initWithNodeName:[NSString stringWithUTF8String:dataSource]
user:auth_user
password:auth_password];
else
engine = [[PathManager alloc] initWithNodeName:[NSString stringWithUTF8String:dataSource]];
}
else if (strcasecmp(dataSource, "eDSLocalHostedNodes") == 0)
{
// open the node via the local DS service.
engine = [[PathManager alloc] initWithNodeEnum:eDSLocalHostedNodes];
}
else if (strcasecmp(dataSource, "eDSLocalNodeNames") == 0)
{
// open the node via the local DS service.
engine = [[PathManager alloc] initWithNodeEnum:eDSLocalNodeNames];
}
else if (streq(dataSource, "."))
{
// The data source is the local node.
if (auth_user && auth_password)
engine = [[PathManager alloc] initWithLocalNodeAuthUser:auth_user
password:auth_password];
else
engine = [[PathManager alloc] initWithLocalNode];
}
else if (strcasecmp(dataSource, "localhost") == 0)
{
// The data source is the localhost, open the local DS service.
engine = [[PathManager alloc] initWithLocal];
}
else if (strcasecmp(dataSource, "localonly") == 0)
{
// The data source is the localonly, open the local only DS service.
engine = [[PathManager alloc] initWithLocalPath:@"Default"];
}
else
{
// assume the data source is a remote host to be contacted via DS proxy.
engine = [[PathManager alloc] initWithHost:[NSString stringWithUTF8String:dataSource]
user:auth_user
password:auth_password];
}
if (engine != nil)
{
i++;
if (i >= argc)
{
status = dscl_interactive(stdin, prompt);
if (prompt != PROMPT_NONE) printf("Goodbye\n");
}
else status = dscl_cmd(argc - i, argv + i);
}
else
{
status = EX_UNAVAILABLE;
printf("Data source (%s) is not valid.\n", dataSource);
}
} @catch( DSoException *exception ) {
if ([[exception name] isEqualToString:@"DSOpenDirServiceErr"]) {
printf("Cannot open remote host, error: DSOpenDirServiceErr\n");
} else {
status = [exception status];
switch ( status )
{
case eServerSendError:
case eServerNotRunning:
if ( isSingleUserMode() ) {
fprintf( stderr, "For Single User Mode you must run the following command to enable use of dscl.\n" );
fprintf( stderr, " launchctl load /System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist\n" );
fprintf( stderr, " dscl localonly\n\n" );
status = EX_CONFIG;
break;
}
default:
fprintf( stderr, "Operation failed with error: %s\n", [[exception statusString] UTF8String] );
status = EX_SOFTWARE;
break;
}
}
} @catch ( id exception ) {
fprintf( stderr, "*** Uncaught Exception: <%s> (%s)\n", [[exception name] UTF8String], [[exception reason] UTF8String] );
status = EX_SOFTWARE;
}
[engine release];
[pool release];
exit(status);
}