]> git.saurik.com Git - apple/configd.git/blobdiff - scutil.tproj/nc.c
configd-395.6.tar.gz
[apple/configd.git] / scutil.tproj / nc.c
diff --git a/scutil.tproj/nc.c b/scutil.tproj/nc.c
new file mode 100644 (file)
index 0000000..48806d0
--- /dev/null
@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 2010-2011 Apple 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@
+ */
+
+/*
+ * Modification History
+ *
+ * March 1, 2010                       Christophe Allie <callie@apple.com>
+ * - initial revision
+ * February 8, 2011                    Kevin Wells <kcw@apple.com>
+ * - added "select" command
+ */
+
+
+#include "scutil.h"
+#include "nc.h"
+#include "prefs.h"
+
+#include <sys/time.h>
+
+
+static SCNetworkConnectionRef  connectionRef   = NULL;
+static int                     n_callback      = 0;
+
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+my_CFRelease(void *t)
+{
+       void * * obj = (void * *)t;
+       if (obj && *obj) {
+               CFRelease(*obj);
+               *obj = NULL;
+       }
+       return;
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static CFStringRef
+nc_copy_serviceID(int argc, char **argv)
+{
+       CFStringRef             serviceIDRef    = NULL;
+
+       if (argc == 0) {
+               serviceIDRef = _copyStringFromSTDIN();
+       } else {
+               serviceIDRef = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+       }
+
+       return serviceIDRef;
+}
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static SCNetworkServiceRef
+nc_copy_service(SCNetworkSetRef set, CFStringRef identifier)
+{
+       CFIndex                 i;
+       CFIndex                 n;
+       SCNetworkServiceRef     selected        = NULL;
+       CFArrayRef              services;
+
+       services = SCNetworkConnectionCopyAvailableServices(set);
+       if (services == NULL) {
+               goto done;
+       }
+
+       n = CFArrayGetCount(services);
+
+       // try to select the service by its serviceID
+       for (i = 0; i < n; i++) {
+               SCNetworkServiceRef     service         = NULL;
+               CFStringRef             serviceID;
+
+               service = CFArrayGetValueAtIndex(services, i);
+               serviceID = SCNetworkServiceGetServiceID(service);
+               if (CFEqual(identifier, serviceID)) {
+                       selected = service;
+                       goto done;
+               }
+       }
+
+       // try to select the service by service name
+       for (i = 0; i < n; i++) {
+               SCNetworkServiceRef     service         = NULL;
+               CFStringRef             serviceName;
+
+               service = CFArrayGetValueAtIndex(services, i);
+               serviceName = SCNetworkServiceGetName(service);
+               if ((serviceName != NULL) && CFEqual(identifier, serviceName)) {
+                       if (selected == NULL) {
+                               selected = service;
+                       } else {
+                               // if multiple services match
+                               selected = NULL;
+                               SCPrint(TRUE, stdout, CFSTR("multiple services match\n"));
+                               goto done;
+                       }
+               }
+       }
+
+    done :
+
+       if (selected != NULL) CFRetain(selected);
+       if (services != NULL) CFRelease(services);
+       return selected;
+}
+
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static char *
+nc_status_string(SCNetworkConnectionStatus status)
+{
+       switch (status) {
+               case kSCNetworkConnectionInvalid:
+                       return "Invalid";
+               case kSCNetworkConnectionDisconnected:
+                       return "Disconnected";
+               case kSCNetworkConnectionConnecting:
+                       return "Connecting";
+               case kSCNetworkConnectionConnected:
+                       return "Connected";
+               case kSCNetworkConnectionDisconnecting:
+                       return "Disconnecting";
+       }
+       return "Unknown";
+}
+
+static void
+nc_callback(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void *info)
+{
+       int             *n              = (int *)info;
+       CFDictionaryRef status_dict;
+
+       // report status
+       if (n != NULL) {
+               if (*n == 0) {
+                       SCPrint(TRUE, stdout, CFSTR("Current status = "));
+               } else {
+                       struct tm       tm_now;
+                       struct timeval  tv_now;
+
+                       (void)gettimeofday(&tv_now, NULL);
+                       (void)localtime_r(&tv_now.tv_sec, &tm_now);
+
+                       SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
+                               tm_now.tm_hour,
+                               tm_now.tm_min,
+                               tm_now.tm_sec,
+                               tv_now.tv_usec / 1000);
+                       SCPrint(TRUE, stdout, CFSTR("Callback (%d) status = "), *n);
+               }
+               *n = *n + 1;
+       }
+       SCPrint(TRUE, stdout, CFSTR("%s%s%s\n"),
+               nc_status_string(status),
+               (status == kSCNetworkConnectionInvalid) ? ": "                     : "",
+               (status == kSCNetworkConnectionInvalid) ? SCErrorString(SCError()) : "");
+
+       // report extended status
+       status_dict = SCNetworkConnectionCopyExtendedStatus(connection);
+       if (status_dict) {
+               SCPrint(TRUE, stdout, CFSTR("Extended Status %@\n"), status_dict);
+               CFRelease(status_dict);
+       }
+
+       return;
+}
+
+static void
+nc_create_connection(int argc, char **argv, Boolean exit_on_failure)
+{
+       SCNetworkConnectionContext      context = { 0, &n_callback, NULL, NULL, NULL };
+       SCNetworkServiceRef             service;
+       CFStringRef                     serviceIDRef;
+
+       serviceIDRef = nc_copy_serviceID(argc, argv);
+       if (serviceIDRef == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service identifier\n"));
+               if (exit_on_failure)
+                       exit(1);
+               return;
+       }
+
+       service = nc_copy_service(NULL, serviceIDRef);
+       CFRelease(serviceIDRef);
+       if (service == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service\n"));
+               if (exit_on_failure)
+                       exit(1);
+               return;
+       }
+
+       connectionRef = SCNetworkConnectionCreateWithService(NULL, service, nc_callback, &context);
+       if (connectionRef == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("nc_create_connection SCNetworkConnectionCreateWithServiceID() failed to create connectionRef: %s\n"), SCErrorString(SCError()));
+               if (exit_on_failure)
+                       exit(1);
+               return;
+       }
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_release_connection()
+{
+       my_CFRelease(&connectionRef);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_start(int argc, char **argv)
+{
+       nc_create_connection(argc, argv, TRUE);
+
+       SCNetworkConnectionStart(connectionRef, 0, TRUE);
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_stop(int argc, char **argv)
+{
+       nc_create_connection(argc, argv, TRUE);
+
+       SCNetworkConnectionStop(connectionRef, TRUE);
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static void
+nc_suspend(int argc, char **argv)
+{
+       nc_create_connection(argc, argv, TRUE);
+
+       SCNetworkConnectionSuspend(connectionRef);
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static void
+nc_resume(int argc, char **argv)
+{
+       nc_create_connection(argc, argv, TRUE);
+
+       SCNetworkConnectionResume(connectionRef);
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_status(int argc, char **argv)
+{
+       SCNetworkConnectionStatus       status;
+
+       nc_create_connection(argc, argv, TRUE);
+
+       status = SCNetworkConnectionGetStatus(connectionRef);
+       nc_callback(connectionRef, status, NULL);
+
+       nc_release_connection();
+       exit(0);
+}
+
+static void
+nc_watch(int argc, char **argv)
+{
+       SCNetworkConnectionStatus       status;
+
+       nc_create_connection(argc, argv, TRUE);
+
+       status = SCNetworkConnectionGetStatus(connectionRef);
+
+       // report initial status
+       n_callback = 0;
+       nc_callback(connectionRef, status, &n_callback);
+
+       // setup watcher
+       if (doDispatch) {
+               if (!SCNetworkConnectionSetDispatchQueue(connectionRef, dispatch_get_current_queue())) {
+                       printf("SCNetworkConnectionSetDispatchQueue() failed: %s\n", SCErrorString(SCError()));
+                       exit(1);
+               }
+       } else {
+               if (!SCNetworkConnectionScheduleWithRunLoop(connectionRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
+                       printf("SCNetworkConnectinScheduleWithRunLoop() failed: %s\n", SCErrorString(SCError()));
+                       exit(1);
+               }
+       }
+
+       // wait for changes
+       CFRunLoopRun();
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_statistics(int argc, char **argv)
+{
+       CFDictionaryRef stats_dict;
+
+       nc_create_connection(argc, argv, TRUE);
+
+       stats_dict = SCNetworkConnectionCopyStatistics(connectionRef);
+
+       if (stats_dict) {
+               SCPrint(TRUE, stdout, CFSTR("%@\n"), stats_dict);
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("No statistics available\n"));
+       }
+
+       my_CFRelease(&stats_dict);
+
+       nc_release_connection();
+       exit(0);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_ondemand(int argc, char **argv)
+{
+       int                     exit_code       = 1;
+       CFStringRef             key             = NULL;
+       CFDictionaryRef         ondemand_dict   = NULL;
+       SCDynamicStoreRef       store;
+
+       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
+       if (store == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("do_nc_ondemand SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+
+       key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
+       if (key == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("do_nc_ondemand SCDynamicStoreKeyCreateNetworkGlobalEntity() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+
+       ondemand_dict = SCDynamicStoreCopyValue(store, key);
+       if (ondemand_dict) {
+               SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
+       }
+
+       exit_code = 0;
+done:
+       my_CFRelease(&ondemand_dict);
+       my_CFRelease(&key);
+       my_CFRelease(&store);
+       exit(exit_code);
+}
+
+/* -----------------------------------------------------------------------------
+ Given a string 'key' and a string prefix 'prefix',
+ return the next component in the slash '/' separated
+ key.  If no slash follows the prefix, return NULL.
+
+ Examples:
+ 1. key = "a/b/c" prefix = "a/"    returns "b"
+ 2. key = "a/b/c" prefix = "a/b/"  returns NULL
+----------------------------------------------------------------------------- */
+CFStringRef parse_component(CFStringRef key, CFStringRef prefix)
+{
+       CFMutableStringRef      comp;
+       CFRange                 range;
+
+       if (!CFStringHasPrefix(key, prefix))
+               return NULL;
+
+       comp = CFStringCreateMutableCopy(NULL, 0, key);
+       CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
+       range = CFStringFind(comp, CFSTR("/"), 0);
+       if (range.location == kCFNotFound) {
+               CFRelease(comp);
+               return NULL;
+       }
+       range.length = CFStringGetLength(comp) - range.location;
+       CFStringDelete(comp, range);
+       return comp;
+}
+
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_list(int argc, char **argv)
+{
+       int                     count;
+       int                     exit_code       = 1;
+       int                     i;
+       CFStringRef             key             = NULL;
+       CFMutableDictionaryRef  names           = NULL;
+       CFArrayRef              services        = NULL;
+       CFStringRef             setup           = NULL;
+       SCDynamicStoreRef       store;
+
+       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
+       if (store == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+       key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface);
+       if (key == NULL ) {
+               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create key string\n"));
+               goto done;
+       }
+       setup = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/"), kSCDynamicStoreDomainSetup, kSCCompNetwork, kSCCompService);
+       if (setup == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreate() failed to create setup string\n"));
+               goto done;
+       }
+       names = CFDictionaryCreateMutable(NULL,
+                                         0,
+                                         &kCFTypeDictionaryKeyCallBacks,
+                                         &kCFTypeDictionaryValueCallBacks);
+       if (names == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("nc_list CFDictionaryCreateMutable() failed to create names dictionary\n"));
+               goto done;
+       }
+       services = SCNetworkConnectionCopyAvailableServices(NULL);
+       if (services != NULL) {
+               count = CFArrayGetCount(services);
+
+               for (i = 0; i < count; i++) {
+                       SCNetworkServiceRef     service;
+                       CFStringRef             serviceID;
+                       CFStringRef             serviceName;
+
+                       service = CFArrayGetValueAtIndex(services, i);
+                       serviceID = SCNetworkServiceGetServiceID(service);
+                       serviceName = SCNetworkServiceGetName(service);
+                       if (serviceName != NULL) {
+                               CFDictionarySetValue(names, serviceID, serviceName);
+                       }
+               }
+
+               CFRelease(services);
+       }
+
+       services = SCDynamicStoreCopyKeyList(store, key);
+       if (services == NULL ) {
+               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCopyKeyList() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+
+       count = CFArrayGetCount(services);
+       for (i = 0; i < count; i++) {
+               CFStringRef serviceID;
+
+               serviceID = parse_component(CFArrayGetValueAtIndex(services, i), setup);
+               if (serviceID) {
+                       CFStringRef     iftype;
+                       CFStringRef     ifsubtype;
+                       CFStringRef     interface_key   = NULL;
+                       CFDictionaryRef interface_dict  = NULL;
+                       CFStringRef     service_name;
+
+                       interface_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface);
+                       if (!interface_key)  {
+                               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreKeyCreateNetworkServiceEntity() failed to interface key string\n"));
+                               goto endloop;
+                       }
+
+                       interface_dict = SCDynamicStoreCopyValue(store, interface_key);
+                       if (!interface_dict) {
+                               SCPrint(TRUE, stderr, CFSTR("nc_list SCDynamicStoreCopyValue() to copy interface dictionary: %s\n"), SCErrorString(SCError()));
+                               goto endloop;
+                       }
+
+                       iftype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceType);
+                       if (!iftype) {
+                               // is that an error condition ???
+                               goto endloop;
+                       }
+
+                       if (!CFEqual(iftype, kSCEntNetPPP) &&
+                               !CFEqual(iftype, kSCEntNetIPSec) &&
+                               !CFEqual(iftype, kSCEntNetVPN))
+                               goto endloop;
+
+                       ifsubtype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceSubType);
+
+                       service_name = CFDictionaryGetValue(names, serviceID);
+
+                       SCPrint(TRUE, stdout, CFSTR("[%@%@%@] %@%s%@\n"),
+                               iftype ? iftype : CFSTR("?"),
+                               ifsubtype ? CFSTR("/") : CFSTR(""),
+                               ifsubtype ? ifsubtype : CFSTR(""),
+                               serviceID,
+                               service_name ? " : " : "",
+                               service_name ? service_name : CFSTR(""));
+
+                   endloop:
+                       my_CFRelease(&interface_key);
+                       my_CFRelease(&interface_dict);
+                       my_CFRelease(&serviceID);
+               }
+       }
+
+       exit_code = 0;
+done:
+       my_CFRelease(&services);
+       my_CFRelease(&names);
+       my_CFRelease(&setup);
+       my_CFRelease(&key);
+       my_CFRelease(&store);
+       exit(exit_code);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static void
+nc_show(int argc, char **argv)
+{
+       SCDynamicStoreRef       store = NULL;
+       int                     exit_code = 1;
+       CFStringRef             setup = NULL;
+       CFStringRef             serviceIDRef = NULL;
+       CFArrayRef              services = NULL;
+       CFStringRef             iftype = NULL;
+       CFStringRef             ifsubtype = NULL;
+       CFStringRef             interface_key = NULL;
+       CFDictionaryRef         interface_dict = NULL;
+       CFStringRef             type_entity_key = NULL;
+       CFStringRef             subtype_entity_key = NULL;
+       CFDictionaryRef         type_entity_dict = NULL;
+       CFDictionaryRef         subtype_entity_dict = NULL;
+
+       serviceIDRef = nc_copy_serviceID(argc, argv);
+       if (serviceIDRef == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service ID\n"));
+               goto done;
+       }
+
+       store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
+       if (store == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+
+       interface_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, kSCEntNetInterface);
+       if (!interface_key) {
+               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create interface key\n"));
+               goto done;
+       }
+
+       interface_dict = SCDynamicStoreCopyValue(store, interface_key);
+       if (!interface_dict) {
+               SCPrint(TRUE, stdout, CFSTR("Interface dictionary missing for service ID : %@\n"), serviceIDRef);
+               goto done;
+       }
+
+       iftype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceType);
+       if (!iftype) {
+               SCPrint(TRUE, stdout, CFSTR("Interface Type missing for service ID : %@\n"), serviceIDRef);
+               goto done;
+       }
+
+       if (!CFEqual(iftype, kSCEntNetPPP) &&
+               !CFEqual(iftype, kSCEntNetIPSec) &&
+               !CFEqual(iftype, kSCEntNetVPN)) {
+               SCPrint(TRUE, stdout, CFSTR("Interface Type [%@] invalid for service ID : %@\n"), iftype, serviceIDRef);
+               goto done;
+       }
+
+       ifsubtype = CFDictionaryGetValue(interface_dict, kSCPropNetInterfaceSubType);
+       SCPrint(TRUE, stdout, CFSTR("[%@%@%@] %@\n"),
+               iftype ? iftype : CFSTR("?"),
+               ifsubtype ? CFSTR("/") : CFSTR(""),
+               ifsubtype ? ifsubtype : CFSTR(""),
+               serviceIDRef);
+
+       type_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, iftype);
+       if (!type_entity_key) {
+               SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create type entity key\n"));
+               goto done;
+       }
+       type_entity_dict = SCDynamicStoreCopyValue(store, type_entity_key);
+       if (!type_entity_dict) {
+               SCPrint(TRUE, stdout, CFSTR("%@ dictionary missing for service ID : %@\n"), iftype, serviceIDRef);
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), iftype, type_entity_dict);
+       }
+
+       if (ifsubtype) {
+               subtype_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceIDRef, ifsubtype);
+               if (!subtype_entity_key) {
+                       SCPrint(TRUE, stderr, CFSTR("nc_show SCDynamicStoreKeyCreateNetworkServiceEntity() failed to create subtype entity key\n"));
+                       goto done;
+               }
+               subtype_entity_dict = SCDynamicStoreCopyValue(store, subtype_entity_key);
+               if (!subtype_entity_dict) {
+                       //
+               }
+               else {
+                       SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), ifsubtype, subtype_entity_dict);
+               }
+       }
+
+       exit_code = 0;
+
+done:
+       my_CFRelease(&serviceIDRef);
+       my_CFRelease(&interface_key);
+       my_CFRelease(&interface_dict);
+       my_CFRelease(&type_entity_key);
+       my_CFRelease(&type_entity_dict);
+       my_CFRelease(&subtype_entity_key);
+       my_CFRelease(&subtype_entity_dict);
+       my_CFRelease(&services);
+       my_CFRelease(&setup);
+       my_CFRelease(&store);
+
+       exit(exit_code);
+}
+
+/* -----------------------------------------------------------------------------
+ ----------------------------------------------------------------------------- */
+static void
+nc_select(int argc, char **argv)
+{
+       SCNetworkSetRef         current_set;
+       int                     exit_code       = 1;
+       SCNetworkServiceRef     service         = NULL;
+       CFStringRef             service_id;
+       Boolean                 status;
+
+       service_id = nc_copy_serviceID(argc, argv);
+       if (service_id == NULL) {
+               SCPrint(TRUE, stderr, CFSTR("No service identifier\n"));
+               exit(exit_code);
+       }
+
+       do_prefs_init();        /* initialization */
+       do_prefs_open(0, NULL); /* open default prefs */
+
+       current_set = SCNetworkSetCopyCurrent(prefs);
+       if (current_set == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkSetCopyCurrent() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+
+       service = nc_copy_service(current_set, service_id);
+       if (service == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("No service\n"));
+               goto done;
+       }
+
+#if !TARGET_OS_IPHONE
+       status = SCNetworkServiceSetEnabled(service, TRUE);
+       if (!status) {
+               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkServiceSetEnabled() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+#else
+       status = SCNetworkSetSetSelectedVPNService(current_set, service);
+       if (!status) {
+               SCPrint(TRUE, stdout, CFSTR("nc_select SCNetworkSetSetSelectedVPNService() failed: %s\n"), SCErrorString(SCError()));
+               goto done;
+       }
+#endif
+
+       _prefs_save();
+       exit_code = 0;
+done:
+
+       my_CFRelease(&service_id);
+       my_CFRelease(&current_set);
+       _prefs_close();
+       exit(exit_code);
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+typedef void (*nc_func) (int argc, char **argv);
+
+static const struct {
+       char            *cmd;
+       nc_func         func;
+} nc_cmds[] = {
+       { "list",               nc_list         },
+       { "ondemand",           nc_ondemand     },
+       { "resume",             nc_resume       },
+       { "select",             nc_select       },
+       { "show",               nc_show         },
+       { "start",              nc_start        },
+       { "statistics",         nc_statistics   },
+       { "status",             nc_status       },
+       { "stop",               nc_stop         },
+       { "suspend",            nc_suspend      },
+};
+#define        N_NC_CMNDS      (sizeof(nc_cmds) / sizeof(nc_cmds[0]))
+
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+int
+find_nc_cmd(char *cmd)
+{
+       int     i;
+
+       for (i = 0; i < (int)N_NC_CMNDS; i++) {
+               if (strcmp(cmd, nc_cmds[i].cmd) == 0) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+void
+do_nc_cmd(char *cmd, int argc, char **argv, Boolean watch)
+{
+       int     i;
+
+       i = find_nc_cmd(cmd);
+       if (i >= 0) {
+               nc_func func;
+
+               func = nc_cmds[i].func;
+               if (watch && (func == nc_status)) {
+                       func = nc_watch;
+               }
+               (*func)(argc, argv);
+       }
+       return;
+}
+