]> git.saurik.com Git - apple/configd.git/blobdiff - scutil.tproj/scutil.c
configd-137.2.tar.gz
[apple/configd.git] / scutil.tproj / scutil.c
index 69cc760d23880598f25b500a4396726437429f2d..8909745fda74e39ab7f684b6a48c4e06f9eccc0b 100644 (file)
@@ -1,28 +1,35 @@
 /*
- * Copyright (c) 2000-2002 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;
@@ -140,127 +189,122 @@ getString(char **line)
 }
 
 
+__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\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, 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;
@@ -274,13 +318,33 @@ main(int argc, char * const argv[])
                        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 :
@@ -289,13 +353,17 @@ main(int argc, char * const argv[])
        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 */
        }
 
@@ -305,38 +373,107 @@ main(int argc, char * const argv[])
                /* 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 */
+       }
+
+       /* are we looking up a preference value */
+       if (get) {
+               if (findPref(get) < 0) {
+                       usage(prog);
+               }
+               do_getPref(get, argc, (char **)argv);
+               /* NOT REACHED */
+       }
+
+       /* are we looking up the proxy configuration */
+       if (proxy) {
+               do_showProxyConfiguration(argc, (char **)argv);
+               /* NOT REACHED */
+       }
+
+       /* are we changing a preference value */
+       if (set) {
+               if (findPref(set) < 0) {
+                       usage(prog);
+               }
+               do_setPref(set, argc, (char **)argv);
+               /* NOT REACHED */
+       }
 
-       /* create a "socket" reference with the file descriptor associated with stdin */
-       in  = CFSocketCreateWithNative(NULL,
-                                      STDIN_FILENO,
-                                      kCFSocketReadCallBack,
-                                      runLoopProcessInput,
-                                      &context);
+       if (net) {
+               /* if we are going to be managing the network configuration */
+               commands  = (cmdInfo *)commands_prefs;
+               nCommands = nCommands_prefs;
 
-       /* Create a run loop source for the (stdin) file descriptor */
-       rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
+               if (!getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
+                       usage(prog);
+               }
 
-       /* keep track of input file sources */
-       sources = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-       CFArrayAppendValue(sources, rls);
+               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;
 
-       /* add this source to the run loop */
-       CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
-       CFRelease(rls);
-       CFRelease(in);
+               do_dictInit(0, NULL);   /* start with an empty dictionary */
+               do_open(0, NULL);       /* open the dynamic store */
+       }
 
-       /* show (initial) debug information, diagnostics */
-       __showMachPortStatus();
+       /* 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.