/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ *
+ * 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
+ * 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@
*/
/*
* Modification History
*
+ * August 4, 2004 Allan Nathanson <ajn@apple.com>
+ * - added network configuration (prefs) support
+ *
+ * September 25, 2002 Allan Nathanson <ajn@apple.com>
+ * - added command line history & editing
+ *
* July 9, 2001 Allan Nathanson <ajn@apple.com>
* - added "-r" option for checking network reachability
* - added "-w" option to check/wait for the presence of a
*/
#include <ctype.h>
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <termios.h>
#include <unistd.h>
#include <sysexits.h>
#include "scutil.h"
#include "commands.h"
#include "dictionary.h"
+#include "net.h"
+#include "prefs.h"
+#include "session.h"
#include "tests.h"
-#include <SystemConfiguration/SCPrivate.h>
#include "SCDynamicStoreInternal.h"
#define LINE_LENGTH 256
-int nesting = 0;
-CFRunLoopSourceRef notifyRls = NULL;
-CFMutableArrayRef sources = NULL;
-SCDynamicStoreRef store = NULL;
-CFPropertyListRef value = NULL;
+__private_extern__ InputRef currentInput = NULL;
+__private_extern__ int nesting = 0;
+__private_extern__ CFRunLoopRef notifyRl = NULL;
+__private_extern__ CFRunLoopSourceRef notifyRls = NULL;
+__private_extern__ SCPreferencesRef prefs = NULL;
+__private_extern__ SCDynamicStoreRef store = NULL;
+__private_extern__ CFPropertyListRef value = NULL;
+__private_extern__ CFMutableArrayRef watchedKeys = NULL;
+__private_extern__ CFMutableArrayRef watchedPatterns = NULL;
+
+static const struct option longopts[] = {
+// { "debug", no_argument, NULL, 'd' },
+// { "verbose", no_argument, NULL, 'v' },
+// { "SPI", no_argument, NULL, 'p' },
+// { "check-reachability", required_argument, NULL, 'r' },
+// { "timeout", required_argument, NULL, 't' },
+// { "wait-key", required_argument, NULL, 'w' },
+ { "dns", no_argument, NULL, 0 },
+ { "get", required_argument, NULL, 0 },
+ { "help", no_argument, NULL, '?' },
+ { "net", no_argument, NULL, 0 },
+ { "proxy", no_argument, NULL, 0 },
+ { "set", required_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
static char *
-getLine(char *buf, int len, FILE *fp)
+getLine(char *buf, int len, InputRef src)
{
- int x;
+ int n;
- if (fgets(buf, len, fp) == NULL)
- return NULL;
+ if (src->el) {
+ int count;
+ const char *line;
- x = strlen(buf);
- if (buf[x-1] == '\n') {
- /* the entire line fit in the buffer, remove the newline */
- buf[x-1] = '\0';
+ line = el_gets(src->el, &count);
+ if (line == NULL)
+ return NULL;
+
+ strncpy(buf, line, len);
} else {
+ if (fgets(buf, len, src->fp) == NULL)
+ return NULL;
+ }
+
+ n = strlen(buf);
+ if (buf[n-1] == '\n') {
+ /* the entire line fit in the buffer, remove the newline */
+ buf[n-1] = '\0';
+ } else if (!src->el) {
/* eat the remainder of the line */
do {
- x = fgetc(fp);
- } while ((x != '\n') && (x != EOF));
+ n = fgetc(src->fp);
+ } while ((n != '\n') && (n != EOF));
}
+ if (src->h) {
+ HistEvent ev;
+
+ history(src->h, &ev, H_ENTER, buf);
+ }
+
+
return buf;
}
-char *
+static char *
getString(char **line)
{
char *s, *e, c, *string;
}
+__private_extern__
Boolean
-process_line(FILE *fp)
+process_line(InputRef src)
{
- char line[LINE_LENGTH], *s, *arg, **argv = NULL;
- int i, argc;
-
- /* if end-of-file, exit */
- if (getLine(line, sizeof(line), fp) == NULL)
+ char *arg;
+ int argc = 0;
+ char **argv = NULL;
+ int i;
+ char line[LINE_LENGTH];
+ char *s = line;
+
+ // if end-of-file, exit
+ if (getLine(line, sizeof(line), src) == NULL)
return FALSE;
if (nesting > 0) {
- SCPrint(TRUE, stdout, CFSTR("%d> %s"), nesting, line);
+ SCPrint(TRUE, stdout, CFSTR("%d> %s\n"), nesting, line);
}
- /* if requested, exit */
- if (strcasecmp(line, "exit") == 0) return FALSE;
- if (strcasecmp(line, "quit") == 0) return FALSE;
- if (strcasecmp(line, "q" ) == 0) return FALSE;
-
- /* break up the input line */
- s = line;
- argc = 0;
+ // break up the input line
while ((arg = getString(&s)) != NULL) {
if (argc == 0)
argv = (char **)malloc(2 * sizeof(char *));
else
- argv = (char **)realloc(argv, ((argc + 2) * sizeof(char *)));
+ argv = (char **)reallocf(argv, ((argc + 2) * sizeof(char *)));
argv[argc++] = arg;
}
- /* process the command */
- if (argc > 0) {
- argv[argc] = NULL; /* just in case... */
-
- if (*argv[0] != '#')
- do_command(argc, argv);
-
- for (i = 0; i < argc; i++)
- free(argv[i]);
- free(argv);
+ if (argc == 0) {
+ return TRUE; // if no arguments
}
- return TRUE;
-}
-
-
-void
-runLoopProcessInput(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
-{
- FILE *fp = info;
-
- if (process_line(fp) == FALSE) {
- /* we don't want any more input from this stream, stop listening */
- CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
- (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
- kCFRunLoopDefaultMode);
-
- /* we no longer need the fd (socket) */
- CFSocketInvalidate(s);
-
- /* we no longer need to track this source */
- CFArrayRemoveValueAtIndex(sources, 0);
-
- if (CFArrayGetCount(sources) > 0) {
- /* add the previous input source to the run loop */
- CFRunLoopAddSource(CFRunLoopGetCurrent(),
- (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
- kCFRunLoopDefaultMode);
- } else {
- /* no more input sources, we're done! */
- exit (EX_OK);
- }
-
- /* decrement the nesting level */
- nesting--;
+ /* process the command */
+ if (*argv[0] != '#') {
+ argv[argc] = NULL; // just in case...
+ currentInput = src;
+ do_command(argc, argv);
}
- /* debug information, diagnostics */
- __showMachPortStatus();
-
- /* if necessary, re-issue prompt */
- if ((CFArrayGetCount(sources) == 1) && isatty(STDIN_FILENO)) {
- printf("> ");
- fflush(stdout);
+ /* free the arguments */
+ for (i = 0; i < argc; i++) {
+ free(argv[i]);
}
+ free(argv);
- return;
+ return !termRequested;
}
-void
+static void
usage(const char *command)
{
SCPrint(TRUE, stderr, CFSTR("usage: %s\n"), command);
- SCPrint(TRUE, stderr, CFSTR(" or: %s -r node-or-address\n"), command);
- SCPrint(TRUE, stderr, CFSTR("\t-r\tcheck reachability of node/address\n"));
+ SCPrint(TRUE, stderr, CFSTR("\tinteractive access to the dynamic store.\n"));
+ SCPrint(TRUE, stderr, CFSTR("\n"));
+ SCPrint(TRUE, stderr, CFSTR(" or: %s -r nodename\n"), command);
+ SCPrint(TRUE, stderr, CFSTR(" or: %s -r address\n"), command);
+ SCPrint(TRUE, stderr, CFSTR(" or: %s -r local-address remote-address\n"), command);
+ SCPrint(TRUE, stderr, CFSTR("\tcheck reachability of node, address, or address pair.\n"));
+ SCPrint(TRUE, stderr, CFSTR("\n"));
SCPrint(TRUE, stderr, CFSTR(" or: %s -w dynamic-store-key [ -t timeout ]\n"), command);
SCPrint(TRUE, stderr, CFSTR("\t-w\twait for presense of dynamic store key\n"));
SCPrint(TRUE, stderr, CFSTR("\t-t\ttime to wait for key\n"));
SCPrint(TRUE, stderr, CFSTR("\n"));
- SCPrint(TRUE, stderr, CFSTR("Note: you may only specify one of \"-r\" or \"-w\".\n"));
+ SCPrint(TRUE, stderr, CFSTR(" or: %s --get pref\n"), command);
+ SCPrint(TRUE, stderr, CFSTR(" or: %s --set pref [newval]\n"), command);
+ SCPrint(TRUE, stderr, CFSTR("\tpref\tdisplay (or set) the specified preference. Valid preferences\n"));
+ SCPrint(TRUE, stderr, CFSTR("\t\tinclude:\n"));
+ SCPrint(TRUE, stderr, CFSTR("\t\t\tComputerName, LocalHostName\n"));
+ SCPrint(TRUE, stderr, CFSTR("\tnewval\tNew preference value to be set. If not specified,\n"));
+ SCPrint(TRUE, stderr, CFSTR("\t\tthe new value will be read from standard input.\n"));
+ SCPrint(TRUE, stderr, CFSTR("\n"));
+ SCPrint(TRUE, stderr, CFSTR(" or: %s --dns\n"), command);
+ SCPrint(TRUE, stderr, CFSTR("\tshow DNS configuration.\n"));
+ SCPrint(TRUE, stderr, CFSTR("\n"));
+ SCPrint(TRUE, stderr, CFSTR(" or: %s --proxy\n"), command);
+ SCPrint(TRUE, stderr, CFSTR("\tshow \"proxy\" configuration.\n"));
+
+ if (getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
+ SCPrint(TRUE, stderr, CFSTR("\n"));
+ SCPrint(TRUE, stderr, CFSTR(" or: %s --net\n"), command);
+ SCPrint(TRUE, stderr, CFSTR("\tmanage network configuration.\n"));
+ }
+
exit (EX_USAGE);
}
+static char *
+prompt(EditLine *el)
+{
+ return "> ";
+}
+
+
int
-main(int argc, const char *argv[])
+main(int argc, char * const argv[])
{
- CFSocketContext context = { 0, stdin, NULL, NULL, NULL };
- char *dest = NULL;
- CFSocketRef in;
+ Boolean dns = FALSE;
+ char *get = NULL;
+ Boolean net = FALSE;
extern int optind;
int opt;
+ int opti;
const char *prog = argv[0];
- CFRunLoopSourceRef rls;
+ Boolean proxy = FALSE;
+ Boolean reach = FALSE;
+ char *set = NULL;
+ InputRef src;
int timeout = 15; /* default timeout (in seconds) */
char *wait = NULL;
+ int xStore = 0; /* non dynamic store command line options */
/* process any arguments */
- while ((opt = getopt(argc, argv, "dvpr:t:w:")) != -1)
+ while ((opt = getopt_long(argc, argv, "dvprt:w:", longopts, &opti)) != -1)
switch(opt) {
case 'd':
_sc_debug = TRUE;
enablePrivateAPI = TRUE;
break;
case 'r':
- dest = optarg;
+ reach = TRUE;
+ xStore++;
break;
case 't':
timeout = atoi(optarg);
break;
case 'w':
wait = optarg;
+ xStore++;
+ break;
+ case 0:
+ if (strcmp(longopts[opti].name, "dns") == 0) {
+ dns = TRUE;
+ xStore++;
+ } else if (strcmp(longopts[opti].name, "get") == 0) {
+ get = optarg;
+ xStore++;
+ } else if (strcmp(longopts[opti].name, "net") == 0) {
+ net = TRUE;
+ xStore++;
+ } else if (strcmp(longopts[opti].name, "proxy") == 0) {
+ proxy = TRUE;
+ xStore++;
+ } else if (strcmp(longopts[opti].name, "set") == 0) {
+ set = optarg;
+ xStore++;
+ }
break;
case '?':
default :
usage(prog);
- }
+ }
argc -= optind;
argv += optind;
- if (dest && wait) {
+ if (xStore > 1) {
+ // if we are attempting to process more than one type of request
usage(prog);
}
/* are we checking the reachability of a host/address */
- if (dest) {
- do_checkReachability(dest);
+ if (reach) {
+ if ((argc < 1) || (argc > 2)) {
+ usage(prog);
+ }
+ do_checkReachability(argc, (char **)argv);
/* NOT REACHED */
}
/* NOT REACHED */
}
- /* start with an empty dictionary */
- do_dictInit(0, NULL);
+ /* are we looking up the DNS configuration */
+ if (dns) {
+ do_showDNSConfiguration(argc, (char **)argv);
+ /* NOT REACHED */
+ }
- /* create a "socket" reference with the file descriptor associated with stdin */
- in = CFSocketCreateWithNative(NULL,
- STDIN_FILENO,
- kCFSocketReadCallBack,
- runLoopProcessInput,
- &context);
+ /* are we looking up a preference value */
+ if (get) {
+ if (findPref(get) < 0) {
+ usage(prog);
+ }
+ do_getPref(get, argc, (char **)argv);
+ /* NOT REACHED */
+ }
- /* Create a run loop source for the (stdin) file descriptor */
- rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
+ /* are we looking up the proxy configuration */
+ if (proxy) {
+ do_showProxyConfiguration(argc, (char **)argv);
+ /* NOT REACHED */
+ }
- /* keep track of input file sources */
- sources = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(sources, rls);
+ /* are we changing a preference value */
+ if (set) {
+ if (findPref(set) < 0) {
+ usage(prog);
+ }
+ do_setPref(set, argc, (char **)argv);
+ /* NOT REACHED */
+ }
+
+ if (net) {
+ /* if we are going to be managing the network configuration */
+ commands = (cmdInfo *)commands_prefs;
+ nCommands = nCommands_prefs;
+
+ if (!getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
+ usage(prog);
+ }
- /* add this source to the run loop */
- CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
- CFRelease(rls);
- CFRelease(in);
+ do_net_init(); /* initialization */
+ do_net_open(0, NULL); /* open default prefs */
+ } else {
+ /* if we are going to be managing the dynamic store */
+ commands = (cmdInfo *)commands_store;
+ nCommands = nCommands_store;
- /* show (initial) debug information, diagnostics */
- __showMachPortStatus();
+ do_dictInit(0, NULL); /* start with an empty dictionary */
+ do_open(0, NULL); /* open the dynamic store */
+ }
+
+ /* allocate command input stream */
+ src = (InputRef)CFAllocatorAllocate(NULL, sizeof(Input), 0);
+ src->fp = stdin;
+ src->el = NULL;
+ src->h = NULL;
+
+ if (isatty(fileno(src->fp))) {
+ int editmode = 1;
+ HistEvent ev;
+ struct termios t;
+
+ if (tcgetattr(fileno(src->fp), &t) != -1) {
+ if ((t.c_lflag & ECHO) == 0) {
+ editmode = 0;
+ }
+ }
+ src->el = el_init(prog, src->fp, stdout, stderr);
+ src->h = history_init();
+
+ (void)history(src->h, &ev, H_SETSIZE, INT_MAX);
+ el_set(src->el, EL_HIST, history, src->h);
+
+ if (!editmode) {
+ el_set(src->el, EL_EDITMODE, 0);
+ }
+
+ el_set(src->el, EL_EDITOR, "emacs");
+ el_set(src->el, EL_PROMPT, prompt);
+
+ el_source(src->el, NULL);
+
+ if ((el_get(src->el, EL_EDITMODE, &editmode) != -1) && editmode != 0) {
+ el_set(src->el, EL_SIGNAL, 1);
+ } else {
+ history_end(src->h);
+ src->h = NULL;
+ el_end(src->el);
+ src->el = NULL;
+ }
+ }
- /* issue (initial) prompt */
- if (isatty(STDIN_FILENO)) {
- printf("> ");
- fflush(stdout);
+ while (process_line(src) == TRUE) {
+ /* debug information, diagnostics */
+ __showMachPortStatus();
}
- CFRunLoopRun(); /* process input, process events */
+ /* close the socket, free resources */
+ if (src->h) history_end(src->h);
+ if (src->el) el_end(src->el);
+ (void)fclose(src->fp);
+ CFAllocatorDeallocate(NULL, src);
exit (EX_OK); // insure the process exit status is 0
return 0; // ...and make main fit the ANSI spec.