]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/nc.c
configd-1061.0.2.tar.gz
[apple/configd.git] / scutil.tproj / nc.c
1 /*
2 * Copyright (c) 2010-2015, 2017-2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * March 1, 2010 Christophe Allie <callie@apple.com>
28 * - initial revision
29 * February 8, 2011 Kevin Wells <kcw@apple.com>
30 * - added "select" command
31 * January 2012 Kevin Wells <kcw@apple.com>
32 * - added arguments to "start" command to pass authentication credentials
33 * - "show" now takes a service name as an alternative to a service ID
34 * - fixes a bug whereby "IPv4" was being displayed as a subtype to IPsec services
35 * - improved format of "list" output
36 * - general cleanup of error messages and some variable names
37 */
38
39
40 #include "scutil.h"
41 #include "nc.h"
42 #include "prefs.h"
43
44 #include <SystemConfiguration/VPNConfiguration.h>
45
46 #include <sys/time.h>
47 #if !TARGET_OS_SIMULATOR
48 #include <NEHelperClient.h>
49 #endif // !TARGET_OS_SIMULATOR
50
51 CFStringRef username = NULL;
52 CFStringRef password = NULL;
53 CFStringRef sharedsecret = NULL;
54
55 static Boolean ondemandwatch = FALSE;
56 static CFStringRef ondemand_nodename = NULL;
57
58 static SCNetworkConnectionRef connection = NULL;
59 static int n_callback = 0;
60
61
62 /* -----------------------------------------------------------------------------
63 ----------------------------------------------------------------------------- */
64 static void
65 my_CFRelease(void *t)
66 {
67 void * * obj = (void * *)t;
68 if (obj && *obj) {
69 CFRelease(*obj);
70 *obj = NULL;
71 }
72 return;
73 }
74
75 /* -----------------------------------------------------------------------------
76 ----------------------------------------------------------------------------- */
77 static void
78 nc_get_service_type_and_subtype(SCNetworkServiceRef service, CFStringRef *iftype, CFStringRef *ifsubtype) {
79 SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface(service);
80 SCNetworkInterfaceRef child = SCNetworkInterfaceGetInterface(interface);
81
82 *iftype = SCNetworkInterfaceGetInterfaceType(interface);
83 *ifsubtype = NULL;
84 if (CFEqual(*iftype, kSCNetworkInterfaceTypePPP) ||
85 CFEqual(*iftype, kSCNetworkInterfaceTypeVPN)) {
86 *ifsubtype = (child != NULL) ? SCNetworkInterfaceGetInterfaceType(child) : NULL;
87 }
88 }
89
90 /* -----------------------------------------------------------------------------
91 ----------------------------------------------------------------------------- */
92 static SCNetworkServiceRef
93 nc_copy_service(SCNetworkSetRef set, CFStringRef identifier)
94 {
95 CFIndex i;
96 CFIndex n;
97 SCNetworkServiceRef selected = NULL;
98 CFArrayRef services;
99
100 services = SCNetworkConnectionCopyAvailableServices(set);
101 if (services == NULL) {
102 goto done;
103 }
104
105 n = CFArrayGetCount(services);
106
107 // try to select the service by its serviceID
108 for (i = 0; i < n; i++) {
109 SCNetworkServiceRef service = NULL;
110 CFStringRef serviceID;
111
112 service = CFArrayGetValueAtIndex(services, i);
113 serviceID = SCNetworkServiceGetServiceID(service);
114 if (CFEqual(identifier, serviceID)) {
115 selected = service;
116 goto done;
117 }
118 }
119
120 // try to select the service by service name
121 for (i = 0; i < n; i++) {
122 SCNetworkServiceRef service = NULL;
123 CFStringRef serviceName;
124
125 service = CFArrayGetValueAtIndex(services, i);
126 serviceName = SCNetworkServiceGetName(service);
127 if ((serviceName != NULL) && CFEqual(identifier, serviceName)) {
128 if (selected == NULL) {
129 selected = service;
130 } else {
131 // if multiple services match
132 selected = NULL;
133 SCPrint(TRUE, stderr, CFSTR("Multiple services match\n"));
134 goto done;
135 }
136 }
137 }
138
139 done :
140
141 if (selected != NULL) CFRetain(selected);
142 if (services != NULL) CFRelease(services);
143 return selected;
144 }
145
146 /* -----------------------------------------------------------------------------
147 ----------------------------------------------------------------------------- */
148 static SCNetworkServiceRef
149 nc_copy_service_from_arguments(int argc, char **argv, SCNetworkSetRef set) {
150 CFStringRef serviceID = NULL;
151 SCNetworkServiceRef service = NULL;
152
153 if (argc == 0) {
154 serviceID = _copyStringFromSTDIN(CFSTR("Service"), NULL);
155 } else {
156 serviceID = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
157 }
158 if (serviceID == NULL) {
159 SCPrint(TRUE, stderr, CFSTR("No service ID specified\n"));
160 return NULL;
161 }
162 service = nc_copy_service(set, serviceID);
163 my_CFRelease(&serviceID);
164 return service;
165 }
166
167
168 /* -----------------------------------------------------------------------------
169 ----------------------------------------------------------------------------- */
170 static char *
171 nc_status_string(SCNetworkConnectionStatus status)
172 {
173 switch (status) {
174 case kSCNetworkConnectionInvalid:
175 return "Invalid";
176 case kSCNetworkConnectionDisconnected:
177 return "Disconnected";
178 case kSCNetworkConnectionConnecting:
179 return "Connecting";
180 case kSCNetworkConnectionConnected:
181 return "Connected";
182 case kSCNetworkConnectionDisconnecting:
183 return "Disconnecting";
184 }
185 return "Unknown";
186 }
187
188 static void
189 nc_callback(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void *info)
190 {
191 int *n = (int *)info;
192 CFDictionaryRef status_dict;
193
194 // report status
195 if (n != NULL) {
196 if (*n == 0) {
197 SCPrint(TRUE, stdout, CFSTR("Current status = "));
198 } else {
199 struct tm tm_now;
200 struct timeval tv_now;
201
202 (void)gettimeofday(&tv_now, NULL);
203 (void)localtime_r(&tv_now.tv_sec, &tm_now);
204
205 SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
206 tm_now.tm_hour,
207 tm_now.tm_min,
208 tm_now.tm_sec,
209 tv_now.tv_usec / 1000);
210 SCPrint(TRUE, stdout, CFSTR("Callback (%d) status = "), *n);
211 }
212 *n = *n + 1;
213 }
214 SCPrint(TRUE, stdout, CFSTR("%s%s%s\n"),
215 nc_status_string(status),
216 (status == kSCNetworkConnectionInvalid) ? ": " : "",
217 (status == kSCNetworkConnectionInvalid) ? SCErrorString(SCError()) : "");
218
219 // report extended status
220 status_dict = SCNetworkConnectionCopyExtendedStatus(connection);
221 if (status_dict) {
222 SCPrint(TRUE, stdout, CFSTR("Extended Status %@\n"), status_dict);
223 CFRelease(status_dict);
224 }
225
226 return;
227 }
228
229 static void
230 nc_create_connection(int argc, char **argv, Boolean exit_on_failure)
231 {
232 SCNetworkConnectionContext context = { 0, &n_callback, NULL, NULL, NULL };
233 SCNetworkServiceRef service;
234
235 service = nc_copy_service_from_arguments(argc, argv, NULL);
236 if (service == NULL) {
237 SCPrint(TRUE, stderr, CFSTR("No service\n"));
238 if (exit_on_failure)
239 exit(1);
240 return;
241 }
242
243 connection = SCNetworkConnectionCreateWithService(NULL, service, nc_callback, &context);
244 CFRelease(service);
245 if (connection == NULL) {
246 SCPrint(TRUE, stderr, CFSTR("Could not create connection: %s\n"), SCErrorString(SCError()));
247 if (exit_on_failure)
248 exit(1);
249 return;
250 }
251 }
252
253 /* -----------------------------------------------------------------------------
254 ----------------------------------------------------------------------------- */
255
256 static void
257 nc_trigger(int argc, char **argv)
258 {
259 Boolean background = FALSE;
260 int i;
261 CFStringRef hostName = NULL;
262 int port = 80;
263
264 for (i = 0; i < 3 && i < argc; i++) {
265 /* Parse host name. Must be first arg. */
266 if (i == 0) {
267 hostName = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
268 continue;
269 }
270
271 /* Check for optional background flag */
272 if (strcmp(argv[i], "background") == 0) {
273 background = TRUE;
274 continue;
275 }
276
277 /* Parse optional port number */
278 CFStringRef str = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
279 if (str) {
280 int num = CFStringGetIntValue(str);
281 if (num) {
282 port = num;
283 }
284 my_CFRelease(&str);
285 }
286 }
287
288 if (hostName) {
289 CFReadStreamRef readStream = NULL;
290 CFWriteStreamRef writeStream = NULL;
291
292 CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostName, port, &readStream, &writeStream);
293
294 if (background) {
295 CFReadStreamSetProperty(readStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
296 CFWriteStreamSetProperty(writeStream, CFSTR("kCFStreamNetworkServiceType"), CFSTR("kCFStreamNetworkServiceTypeBackground"));
297 }
298
299 if (readStream && writeStream) {
300 CFReadStreamOpen(readStream);
301 CFWriteStreamOpen(writeStream);
302 SCPrint(TRUE, stdout, CFSTR("Opened stream to %@, port %d%s\n"), hostName, port, background ? ", background traffic class" : "");
303 sleep(1);
304 }
305
306 my_CFRelease(&readStream);
307 my_CFRelease(&writeStream);
308 } else {
309 SCPrint(TRUE, stderr, CFSTR("Invalid or missing host name\n"));
310 }
311
312 my_CFRelease(&hostName);
313
314 exit(0);
315 }
316
317 /* -----------------------------------------------------------------------------
318 ----------------------------------------------------------------------------- */
319 static void
320 nc_release_connection()
321 {
322 my_CFRelease(&connection);
323 }
324
325 /* -----------------------------------------------------------------------------
326 ----------------------------------------------------------------------------- */
327 static void
328 nc_start(int argc, char **argv)
329 {
330 CFMutableDictionaryRef userOptions = NULL;
331 CFStringRef iftype = NULL;
332 CFStringRef ifsubtype = NULL;
333 SCNetworkServiceRef service = NULL;
334
335 nc_create_connection(argc, argv, TRUE);
336
337 service = SCNetworkConnectionGetService(connection);
338 nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
339
340 userOptions = CFDictionaryCreateMutable(NULL, 0,
341 &kCFTypeDictionaryKeyCallBacks,
342 &kCFTypeDictionaryValueCallBacks);
343
344 Boolean isL2TP = (CFEqual(iftype, kSCEntNetPPP) &&
345 (ifsubtype != NULL) && CFEqual(ifsubtype, kSCValNetInterfaceSubTypeL2TP));
346
347 if (CFEqual(iftype, kSCEntNetPPP)) {
348 CFMutableDictionaryRef pppEntity = CFDictionaryCreateMutable(NULL, 0,
349 &kCFTypeDictionaryKeyCallBacks,
350 &kCFTypeDictionaryValueCallBacks);
351
352 if (username != NULL) {
353 CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthName, username);
354 }
355 if (password != NULL) {
356 CFDictionarySetValue(pppEntity, kSCPropNetPPPAuthPassword, password);
357 }
358 CFDictionarySetValue(userOptions, kSCEntNetPPP, pppEntity);
359 my_CFRelease(&pppEntity);
360 }
361 if (CFEqual(iftype, kSCEntNetIPSec) || isL2TP) {
362 CFMutableDictionaryRef ipsecEntity = CFDictionaryCreateMutable(NULL, 0,
363 &kCFTypeDictionaryKeyCallBacks,
364 &kCFTypeDictionaryValueCallBacks);
365 if (!isL2TP) {
366 if (username != NULL) {
367 CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthName, username);
368 }
369 if (password != NULL) {
370 CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecXAuthPassword, password);
371 }
372 }
373 if (sharedsecret != NULL) {
374 CFDictionarySetValue(ipsecEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
375 }
376 CFDictionarySetValue(userOptions, kSCEntNetIPSec, ipsecEntity);
377 my_CFRelease(&ipsecEntity);
378 }
379 if (CFEqual(iftype, kSCEntNetVPN)) {
380 CFMutableDictionaryRef vpnEntity = CFDictionaryCreateMutable(NULL, 0,
381 &kCFTypeDictionaryKeyCallBacks,
382 &kCFTypeDictionaryValueCallBacks);
383 if (username != NULL) {
384 CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthName, username);
385 }
386 if (password != NULL) {
387 CFDictionarySetValue(vpnEntity, kSCPropNetVPNAuthPassword, password);
388 }
389 CFDictionarySetValue(userOptions, kSCEntNetVPN, vpnEntity);
390 my_CFRelease(&vpnEntity);
391 }
392 // If it doesn't match any VPN type, fail silently
393
394 if (!SCNetworkConnectionStart(connection, userOptions, TRUE)) {
395 SCPrint(TRUE, stderr, CFSTR("Could not start connection: %s\n"), SCErrorString(SCError()));
396 exit(1);
397 };
398
399 CFRelease(userOptions);
400 nc_release_connection();
401 exit(0);
402 }
403
404 /* -----------------------------------------------------------------------------
405 ----------------------------------------------------------------------------- */
406 static void
407 nc_stop(int argc, char **argv)
408 {
409 nc_create_connection(argc, argv, TRUE);
410
411 if (!SCNetworkConnectionStop(connection, TRUE)) {
412 SCPrint(TRUE, stderr, CFSTR("Could not stop connection: %s\n"), SCErrorString(SCError()));
413 exit(1);
414 };
415
416 nc_release_connection();
417 exit(0);
418 }
419
420 /* -----------------------------------------------------------------------------
421 ----------------------------------------------------------------------------- */
422 static void
423 nc_suspend(int argc, char **argv)
424 {
425 nc_create_connection(argc, argv, TRUE);
426
427 SCNetworkConnectionSuspend(connection);
428
429 nc_release_connection();
430 exit(0);
431 }
432
433 /* -----------------------------------------------------------------------------
434 ----------------------------------------------------------------------------- */
435 static void
436 nc_resume(int argc, char **argv)
437 {
438 nc_create_connection(argc, argv, TRUE);
439
440 SCNetworkConnectionResume(connection);
441
442 nc_release_connection();
443 exit(0);
444 }
445
446 /* -----------------------------------------------------------------------------
447 ----------------------------------------------------------------------------- */
448 static void
449 nc_status(int argc, char **argv)
450 {
451 SCNetworkConnectionStatus status;
452
453 nc_create_connection(argc, argv, TRUE);
454
455 status = SCNetworkConnectionGetStatus(connection);
456 nc_callback(connection, status, NULL);
457
458 nc_release_connection();
459 exit(0);
460 }
461
462 static void
463 nc_watch(int argc, char **argv)
464 {
465 SCNetworkConnectionStatus status;
466
467 nc_create_connection(argc, argv, TRUE);
468
469 status = SCNetworkConnectionGetStatus(connection);
470
471 // report initial status
472 n_callback = 0;
473 nc_callback(connection, status, &n_callback);
474
475 // setup watcher
476 if (doDispatch) {
477 if (!SCNetworkConnectionSetDispatchQueue(connection, dispatch_get_main_queue())) {
478 SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
479 exit(1);
480 }
481 } else {
482 if (!SCNetworkConnectionScheduleWithRunLoop(connection, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
483 SCPrint(TRUE, stderr, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
484 exit(1);
485 }
486 }
487
488 // wait for changes
489 CFRunLoopRun();
490
491 nc_release_connection();
492 exit(0);
493 }
494
495 /* -----------------------------------------------------------------------------
496 ----------------------------------------------------------------------------- */
497 static void
498 nc_statistics(int argc, char **argv)
499 {
500 CFDictionaryRef stats_dict;
501
502 nc_create_connection(argc, argv, TRUE);
503
504 stats_dict = SCNetworkConnectionCopyStatistics(connection);
505
506 if (stats_dict) {
507 SCPrint(TRUE, stdout, CFSTR("%@\n"), stats_dict);
508 } else {
509 SCPrint(TRUE, stdout, CFSTR("No statistics available\n"));
510 }
511
512 my_CFRelease(&stats_dict);
513
514 nc_release_connection();
515 exit(0);
516 }
517
518 /* -----------------------------------------------------------------------------
519 ----------------------------------------------------------------------------- */
520 static void
521 checkOnDemandHost(SCDynamicStoreRef store, CFStringRef nodeName, Boolean retry)
522 {
523 Boolean ok;
524 CFStringRef connectionServiceID = NULL;
525 SCNetworkConnectionStatus connectionStatus = 0;
526 CFStringRef vpnRemoteAddress = NULL;
527
528 SCPrint(TRUE, stdout, CFSTR("OnDemand host/domain check (%sretry)\n"), retry ? "" : "no ");
529
530 ok = __SCNetworkConnectionCopyOnDemandInfoWithName(&store,
531 nodeName,
532 retry,
533 &connectionServiceID,
534 &connectionStatus,
535 &vpnRemoteAddress);
536
537 if (ok) {
538 SCPrint(TRUE, stdout, CFSTR(" serviceID = %@\n"), connectionServiceID);
539 SCPrint(TRUE, stdout, CFSTR(" remote address = %@\n"), vpnRemoteAddress);
540 } else if (SCError() != kSCStatusOK) {
541 SCPrint(TRUE, stdout, CFSTR("%sretry\n"), retry ? "" : "no ");
542 SCPrint(TRUE, stdout,
543 CFSTR(" Unable to copy OnDemand information for connection: %s\n"),
544 SCErrorString(SCError()));
545 } else {
546 SCPrint(TRUE, stdout, CFSTR(" no match\n"));
547 }
548
549 if (connectionServiceID != NULL) {
550 CFRelease(connectionServiceID);
551 connectionServiceID = NULL;
552 }
553 if (vpnRemoteAddress != NULL) {
554 CFRelease(vpnRemoteAddress);
555 vpnRemoteAddress = NULL;
556 }
557
558 return;
559 }
560
561 static void
562 nc_ondemand_callback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
563 {
564 #pragma unused(info)
565 CFStringRef key = NULL;
566 CFDictionaryRef ondemand_dict = NULL;
567 struct tm tm_now;
568 struct timeval tv_now;
569
570 if (CFArrayGetCount(changedKeys) < 1) {
571 return;
572 }
573
574 (void)gettimeofday(&tv_now, NULL);
575 (void)localtime_r(&tv_now.tv_sec, &tm_now);
576
577 SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
578 tm_now.tm_hour,
579 tm_now.tm_min,
580 tm_now.tm_sec,
581 tv_now.tv_usec / 1000);
582
583 if (ondemand_nodename) {
584 checkOnDemandHost(store, ondemand_nodename, FALSE);
585 checkOnDemandHost(store, ondemand_nodename, TRUE);
586 } else {
587 key = CFArrayGetValueAtIndex(changedKeys, 0);
588
589 ondemand_dict = SCDynamicStoreCopyValue(store, key);
590 if (ondemand_dict) {
591 SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
592 } else {
593 SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
594 }
595
596 my_CFRelease(&ondemand_dict);
597 }
598 }
599
600 static void
601 nc_ondemand(int argc, char **argv)
602 {
603 int exit_code = 1;
604 CFStringRef key = NULL;
605 CFDictionaryRef ondemand_dict = NULL;
606 SCDynamicStoreRef store;
607
608 store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), nc_ondemand_callback, NULL);
609 if (store == NULL) {
610 SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
611 goto done;
612 }
613
614 if (argc == 1) {
615 #if !TARGET_OS_SIMULATOR
616 if (strcmp("--refresh", argv[0]) == 0) {
617 SCNetworkConnectionRef connection = NULL;
618
619 connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
620 if (connection && SCNetworkConnectionRefreshOnDemandState(connection)) {
621 exit_code = 0;
622 }
623
624 if (exit_code) {
625 SCPrint(TRUE, stderr, CFSTR("Unable to refresh OnDemand state: %s\n"), SCErrorString(SCError()));
626 }
627
628 my_CFRelease(&connection);
629 goto done;
630 }
631 #endif // !TARGET_OS_SIMULATOR
632
633 ondemand_nodename = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
634 } else if (argc != 0) {
635 SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc ondemand [-W] [hostname]\n"
636 " scutil --nc ondemand -- --refresh\n"));
637 goto done;
638 }
639
640 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
641
642 if (ondemand_nodename) {
643 checkOnDemandHost(store, ondemand_nodename, FALSE);
644 checkOnDemandHost(store, ondemand_nodename, TRUE);
645 } else {
646 ondemand_dict = SCDynamicStoreCopyValue(store, key);
647 if (ondemand_dict) {
648 SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), kSCEntNetOnDemand, ondemand_dict);
649 } else {
650 SCPrint(TRUE, stdout, CFSTR("%@ not configured\n"), kSCEntNetOnDemand);
651 }
652 }
653
654 if (ondemandwatch) {
655 CFMutableArrayRef keys = NULL;
656
657 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
658 CFArrayAppendValue(keys, key);
659 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
660
661 my_CFRelease(&keys);
662
663 SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue());
664
665 CFRunLoopRun();
666 }
667
668 exit_code = 0;
669 done:
670 my_CFRelease(&ondemand_dict);
671 my_CFRelease(&key);
672 my_CFRelease(&store);
673 my_CFRelease(&ondemand_nodename);
674 exit(exit_code);
675 }
676
677
678 /* -----------------------------------------------------------------------------
679 ----------------------------------------------------------------------------- */
680 CFStringRef
681 copy_padded_string(CFStringRef original, int width, CFStringRef prefix, CFStringRef suffix)
682 {
683 CFMutableStringRef padded;
684
685 padded = CFStringCreateMutable(NULL, 0);
686 if (prefix != NULL) {
687 CFStringAppend(padded, prefix);
688 }
689 if (original != NULL) {
690 CFStringAppend(padded, original);
691 }
692 if (suffix != NULL) {
693 CFStringAppend(padded, suffix);
694 }
695 CFStringPad(padded, CFSTR(" "), MAX(CFStringGetLength(original), width), 0);
696 return padded;
697 }
698
699 CFStringRef
700 copy_VPN_status(SCNetworkServiceRef service)
701 {
702 CFStringRef output = NULL;
703 SCNetworkConnectionStatus status = kSCNetworkConnectionInvalid;
704 SCNetworkConnectionRef service_connection = NULL;
705
706 /* Only calculate status is the service is enabled. Default is invalid. */
707 if (SCNetworkServiceGetEnabled(service)) {
708 service_connection = SCNetworkConnectionCreateWithService(NULL, service, NULL, NULL);
709 if (service_connection == NULL) goto done;
710 status = SCNetworkConnectionGetStatus(service_connection);
711 }
712
713 output = CFStringCreateWithCString(NULL, nc_status_string(status), kCFStringEncodingUTF8);
714
715 done:
716 my_CFRelease(&service_connection);
717 return output;
718 }
719
720 static void
721 nc_print_VPN_service(SCNetworkServiceRef service)
722 {
723 SCNetworkInterfaceRef interface = NULL;
724 CFStringRef display_name = NULL;
725 CFStringRef display_name_padded = NULL;
726 CFStringRef service_id = NULL;
727 CFStringRef service_name = NULL;
728 CFStringRef service_name_padded = NULL;
729 CFStringRef service_status = NULL;
730 CFStringRef service_status_padded = NULL;
731 CFStringRef sub_type = NULL;
732 CFStringRef type = NULL;
733
734 nc_get_service_type_and_subtype(service, &type, &sub_type);
735
736 service_name = SCNetworkServiceGetName(service);
737 service_name_padded = copy_padded_string(service_name, 32, CFSTR("\""), CFSTR("\""));
738
739 service_id = SCNetworkServiceGetServiceID(service);
740
741 interface = SCNetworkServiceGetInterface(service);
742 display_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
743 display_name_padded = copy_padded_string(display_name, 18, NULL, NULL);
744
745 service_status = copy_VPN_status(service);
746 service_status_padded = copy_padded_string(service_status, 16, CFSTR("("), CFSTR(")"));
747
748 SCPrint(TRUE,
749 stdout,
750 CFSTR("%@ %@ %@ %@ %@ [%@%@%@]\n"),
751 SCNetworkServiceGetEnabled(service) ? CFSTR("*") : CFSTR(" "),
752 service_status_padded,
753 service_id,
754 display_name_padded,
755 service_name_padded,
756 type,
757 (sub_type == NULL) ? CFSTR("") : CFSTR(":"),
758 (sub_type == NULL) ? CFSTR("") : sub_type);
759
760 CFRelease(display_name_padded);
761 CFRelease(service_name_padded);
762 CFRelease(service_status_padded);
763 my_CFRelease(&service_status);
764 }
765
766
767 /* -----------------------------------------------------------------------------
768 ----------------------------------------------------------------------------- */
769 static void
770 nc_list(int argc, char **argv)
771 {
772 #pragma unused(argc)
773 #pragma unused(argv)
774 CFIndex count;
775 CFIndex i;
776 CFArrayRef services = NULL;
777
778 SCPrint(TRUE, stdout, CFSTR("Available network connection services in the current set (*=enabled):\n"));
779 services = SCNetworkConnectionCopyAvailableServices(NULL);
780 if (services != NULL) {
781 count = CFArrayGetCount(services);
782 for (i = 0; i < count; i++) {
783 SCNetworkServiceRef service;
784
785 service = CFArrayGetValueAtIndex(services, i);
786 nc_print_VPN_service(service);
787 }
788
789 }
790 my_CFRelease(&services);
791 exit(0);
792 }
793
794 /* -----------------------------------------------------------------------------
795 ----------------------------------------------------------------------------- */
796 static Boolean
797 nc_enable_vpntype(CFStringRef vpnType)
798 {
799 Boolean is_enabled = FALSE;
800 Boolean success = FALSE;
801
802 if (vpnType == NULL) {
803 SCPrint(TRUE, stderr, CFSTR("No VPN type provided\n"));
804 goto done;
805 }
806
807 is_enabled = VPNConfigurationIsVPNTypeEnabled(vpnType);
808
809 if (is_enabled) {
810 SCPrint(TRUE, stdout, CFSTR("VPN is already enabled\n"));
811 } else {
812 #if !TARGET_OS_IPHONE
813 AuthorizationRef authorization;
814
815 authorization = _prefs_AuthorizationCreate();
816 if ((authorization == NULL) ||
817 !VPNConfigurationSetAuthorization(authorization)) {
818 SCPrint(TRUE, stderr, CFSTR("VPNConfigurationSetAuthorization failed: %s\n"), SCErrorString(SCError()));
819 goto done;
820 }
821 #endif // !TARGET_OS_IPHONE
822
823 if (!VPNConfigurationEnableVPNType(vpnType)) {
824 SCPrint(TRUE, stderr, CFSTR("VPN could not be enabled: %s\n"), SCErrorString(SCError()));
825 goto done;
826 }
827
828 #if !TARGET_OS_IPHONE
829 _prefs_AuthorizationFree(authorization);
830 #endif // !TARGET_OS_IPHONE
831
832 SCPrint(TRUE, stdout, CFSTR("VPN enabled\n"));
833 }
834 success = TRUE;
835
836 done:
837 return success;
838 }
839
840 /* Turns a service ID or name into a vendor type, or preserves type */
841 static CFStringRef
842 nc_copy_vendor_type (CFStringRef input)
843 {
844 SCNetworkInterfaceRef child;
845 SCNetworkInterfaceRef interface;
846 CFStringRef output_name = input;
847 SCNetworkServiceRef service = NULL;
848 CFStringRef type;
849
850 if (input == NULL) {
851 goto done;
852 }
853
854 service = nc_copy_service(NULL, input);
855 if (service != NULL) {
856 interface = SCNetworkServiceGetInterface(service);
857 child = SCNetworkInterfaceGetInterface(interface);
858 type = SCNetworkInterfaceGetInterfaceType(interface);
859
860 /* Must be of type VPN */
861 if (!CFEqual(type, kSCNetworkInterfaceTypeVPN)) {
862 output_name = NULL;
863 goto done;
864 }
865 output_name = SCNetworkInterfaceGetInterfaceType(child);
866 goto done;
867 }
868
869 done :
870 if (output_name != NULL) CFRetain(output_name);
871 my_CFRelease(&service);
872 return output_name;
873 }
874
875 /* -----------------------------------------------------------------------------
876 ----------------------------------------------------------------------------- */
877 #if !TARGET_OS_IPHONE
878 static const CFStringRef PREF_PREFIX = CFSTR("VPN-");
879 static const CFStringRef PREF_SUFFIX = CFSTR(".plist");
880 static void
881 nc_set_application_url(CFStringRef subtype, CFStringRef directory)
882 {
883 CFURLRef directory_url = NULL;
884 CFDataRef directory_url_data = NULL;
885 CFStringRef vpnprefpath = NULL;
886 char *path = NULL;
887 CFIndex path_len = 0;
888
889 if (subtype == NULL || directory == NULL) {
890 goto done;
891 }
892
893 directory_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
894 directory,
895 kCFURLPOSIXPathStyle,
896 FALSE);
897 if (directory_url == NULL) {
898 SCPrint(TRUE, stderr, CFSTR("CFURLCreateWithFileSystemPath failed\n"));
899 goto done;
900 }
901
902 directory_url_data = CFURLCreateBookmarkData(NULL, directory_url, 0, 0, 0, 0);
903 if (directory_url_data == NULL) {
904 SCPrint(TRUE, stderr, CFSTR("CFURLCreateBookmarkData failed\n"));
905 goto done;
906 }
907
908 vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, subtype, PREF_SUFFIX );
909 if (vpnprefpath == NULL) {
910 SCPrint(TRUE, stderr, CFSTR("CFStringCreateWithFormat failed\n"));
911 goto done;
912 }
913
914 path_len = CFStringGetLength(vpnprefpath) + 1;
915 path = malloc(path_len);
916 if (path == NULL) {
917 goto done;
918 }
919
920 if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
921 SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
922 goto done;
923 }
924
925 do_prefs_init(); /* initialization */
926 do_prefs_open(1, &path); /* open prefs */
927
928 SCPreferencesSetValue(prefs, CFSTR("ApplicationURL"), directory_url_data);
929
930 _prefs_save();
931
932 done:
933 my_CFRelease(&directory_url);
934 my_CFRelease(&directory_url_data);
935 my_CFRelease(&vpnprefpath);
936 if (path) {
937 free(path);
938 }
939 _prefs_close();
940
941 exit(0);
942 }
943 #endif // !TARGET_OS_IPHONE
944
945 /* -----------------------------------------------------------------------------
946 ----------------------------------------------------------------------------- */
947 static void
948 nc_enablevpn(int argc, char **argv)
949 {
950 CFStringRef argument = NULL;
951 CFStringRef vendorType = NULL;
952 int exit_code = 1;
953
954 if (argc == 0) {
955 SCPrint(TRUE, stderr, CFSTR("No service type or ID\n"));
956 } else {
957 argument = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
958 vendorType = nc_copy_vendor_type(argument);
959 my_CFRelease(&argument);
960
961 if (!nc_enable_vpntype(vendorType)) {
962 goto done;
963 }
964 #if !TARGET_OS_IPHONE
965 if (argc >= 2) {
966 argument = CFStringCreateWithCString(NULL, argv[1], kCFStringEncodingUTF8);
967 nc_set_application_url(vendorType, argument);
968 my_CFRelease(&argument);
969 }
970 #endif // !TARGET_OS_IPHONE
971 }
972
973 exit_code = 0;
974
975 done:
976 my_CFRelease(&vendorType);
977 exit(exit_code);
978 }
979
980 /* -----------------------------------------------------------------------------
981 ----------------------------------------------------------------------------- */
982 static void
983 nc_show(int argc, char **argv)
984 {
985 SCNetworkServiceRef service = NULL;
986 SCDynamicStoreRef store = NULL;
987 int exit_code = 1;
988 CFStringRef serviceID = NULL;
989 CFStringRef iftype = NULL;
990 CFStringRef ifsubtype = NULL;
991 CFStringRef type_entity_key = NULL;
992 CFStringRef subtype_entity_key = NULL;
993 CFDictionaryRef type_entity_dict = NULL;
994 CFDictionaryRef subtype_entity_dict = NULL;
995 CFStringRef vpnprefpath = NULL;
996 #if !TARGET_OS_IPHONE
997 CFDataRef bookmarkData = NULL;
998 CFURLRef directory = NULL;
999 Boolean isStale = FALSE;
1000 char *path = NULL;
1001 CFIndex path_len = 0;
1002 #endif // !TARGET_OS_IPHONE
1003
1004 service = nc_copy_service_from_arguments(argc, argv, NULL);
1005 if (service == NULL) {
1006 SCPrint(TRUE, stderr, CFSTR("No service\n"));
1007 exit(exit_code);
1008 }
1009
1010 if (!_SCNetworkServiceIsVPN(service)) {
1011 SCPrint(TRUE, stderr, CFSTR("Not a connection oriented service: %@\n"), serviceID);
1012 goto done;
1013 }
1014
1015 serviceID = SCNetworkServiceGetServiceID(service);
1016
1017 nc_get_service_type_and_subtype(service, &iftype, &ifsubtype);
1018
1019 type_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, iftype);
1020
1021 nc_print_VPN_service(service);
1022
1023 #if !TARGET_OS_IPHONE
1024 vpnprefpath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@"), PREF_PREFIX, ifsubtype, PREF_SUFFIX);
1025 if (vpnprefpath == NULL) {
1026 goto skipURL;
1027 }
1028
1029 path_len = CFStringGetLength(vpnprefpath) + 1;
1030 path = malloc(path_len);
1031 if (path == NULL) {
1032 goto skipURL;
1033 }
1034
1035 if (!CFStringGetCString(vpnprefpath, path, path_len, kCFStringEncodingASCII)) {
1036 SCPrint(TRUE, stderr, CFSTR("CFStringGetCString failed\n"));
1037 goto done;
1038 }
1039
1040 do_prefs_init(); /* initialization */
1041 do_prefs_open(1, &path); /* open prefs */
1042
1043 bookmarkData = SCPreferencesGetValue(prefs, CFSTR("ApplicationURL"));
1044 if (bookmarkData == NULL) {
1045 goto skipURL;
1046 }
1047
1048 directory = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkData, 0, NULL, NULL, &isStale, NULL);
1049 if (directory == NULL) {
1050 goto skipURL;
1051 }
1052
1053 SCPrint(TRUE, stdout, CFSTR("ApplicationURL: %@\n"), directory);
1054 skipURL:
1055 #endif // !TARGET_OS_IPHONE
1056
1057 store = SCDynamicStoreCreate(NULL, CFSTR("scutil --nc"), NULL, NULL);
1058 if (store == NULL) {
1059 SCPrint(TRUE, stderr, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
1060 goto done;
1061 }
1062 type_entity_dict = SCDynamicStoreCopyValue(store, type_entity_key);
1063
1064 if (!type_entity_dict) {
1065 SCPrint(TRUE, stderr, CFSTR("No \"%@\" configuration available\n"), iftype);
1066 } else {
1067 SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), iftype, type_entity_dict);
1068 }
1069
1070 if (ifsubtype) {
1071 subtype_entity_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, ifsubtype);
1072 subtype_entity_dict = SCDynamicStoreCopyValue(store, subtype_entity_key);
1073 if (!subtype_entity_dict) {
1074 //
1075 }
1076 else {
1077 SCPrint(TRUE, stdout, CFSTR("%@ %@\n"), ifsubtype, subtype_entity_dict);
1078 }
1079 }
1080
1081 exit_code = 0;
1082
1083 done:
1084 my_CFRelease(&type_entity_key);
1085 my_CFRelease(&type_entity_dict);
1086 my_CFRelease(&subtype_entity_key);
1087 my_CFRelease(&subtype_entity_dict);
1088 my_CFRelease(&store);
1089 my_CFRelease(&service);
1090 my_CFRelease(&vpnprefpath);
1091 _prefs_close();
1092 exit(exit_code);
1093 }
1094
1095 /* -----------------------------------------------------------------------------
1096 ----------------------------------------------------------------------------- */
1097 static void
1098 nc_select(int argc, char **argv)
1099 {
1100 SCNetworkSetRef current_set;
1101 int exit_code = 1;
1102 SCNetworkServiceRef service = NULL;
1103 #if NE_HAS_ENABLE_VPN
1104 uuid_string_t config_id_string;
1105 uuid_t config_id;
1106 #else // NE_HAS_ENABLE_VPN
1107 Boolean status;
1108 #endif // NE_HAS_ENABLE_VPN
1109
1110 do_prefs_init(); /* initialization */
1111 do_prefs_open(0, NULL); /* open default prefs */
1112
1113 current_set = SCNetworkSetCopyCurrent(prefs);
1114 if (current_set == NULL) {
1115 SCPrint(TRUE, stderr, CFSTR("No current location\n"));
1116 goto done;
1117 }
1118
1119 service = nc_copy_service_from_arguments(argc, argv, current_set);
1120 if (service == NULL) {
1121 SCPrint(TRUE, stderr, CFSTR("No service\n"));
1122 goto done;
1123 }
1124
1125 #if NE_HAS_ENABLE_VPN
1126 memset(config_id_string, 0, sizeof(config_id_string));
1127 if (_SC_cfstring_to_cstring(SCNetworkServiceGetServiceID(service), config_id_string, sizeof(config_id_string), kCFStringEncodingUTF8) != NULL &&
1128 uuid_parse(config_id_string, config_id) == 0)
1129 {
1130 if (!NEHelperVPNSetEnabled(config_id, true)) {
1131 SCPrint(TRUE, stderr, CFSTR("Unable to enable service\n"));
1132 }
1133 } else {
1134 SCPrint(TRUE, stderr, CFSTR("Invalid service ID: %@\n"), SCNetworkServiceGetServiceID(service));
1135 }
1136 #else // NE_HAS_ENABLE_VPN
1137 #if !TARGET_OS_IPHONE
1138 status = SCNetworkServiceSetEnabled(service, TRUE);
1139 if (!status) {
1140 SCPrint(TRUE, stderr, CFSTR("Unable to enable service: %s\n"), SCErrorString(SCError()));
1141 goto done;
1142 }
1143 #else // !TARGET_OS_IPHONE
1144 status = SCNetworkSetSetSelectedVPNService(current_set, service);
1145 if (!status) {
1146 SCPrint(TRUE, stderr, CFSTR("Unable to select service: %s\n"), SCErrorString(SCError()));
1147 goto done;
1148 }
1149 #endif // !TARGET_OS_IPHONE
1150
1151 _prefs_save();
1152 #endif // NE_HAS_ENABLE_VPN
1153 exit_code = 0;
1154 done:
1155 my_CFRelease(&service);
1156 my_CFRelease(&current_set);
1157 _prefs_close();
1158 exit(exit_code);
1159 }
1160
1161 /* -----------------------------------------------------------------------------
1162 ----------------------------------------------------------------------------- */
1163 static void
1164 nc_help(int argc, char **argv)
1165 {
1166 #pragma unused(argc)
1167 #pragma unused(argv)
1168 SCPrint(TRUE, stderr, CFSTR("Valid commands for scutil --nc (VPN connections)\n"));
1169 SCPrint(TRUE, stderr, CFSTR("Usage: scutil --nc [command]\n"));
1170 SCPrint(TRUE, stderr, CFSTR("\n"));
1171 SCPrint(TRUE, stderr, CFSTR("\tlist\n"));
1172 SCPrint(TRUE, stderr, CFSTR("\t\tList available network connection services in the current set\n"));
1173 SCPrint(TRUE, stderr, CFSTR("\n"));
1174 SCPrint(TRUE, stderr, CFSTR("\tstatus <service>\n"));
1175 SCPrint(TRUE, stderr, CFSTR("\t\tIndicate whether a given service is connected, as well as extended status information for the service\n"));
1176 SCPrint(TRUE, stderr, CFSTR("\n"));
1177 SCPrint(TRUE, stderr, CFSTR("\tshow <service>\n"));
1178 SCPrint(TRUE, stderr, CFSTR("\t\tDisplay configuration information for a given service\n"));
1179 SCPrint(TRUE, stderr, CFSTR("\n"));
1180 SCPrint(TRUE, stderr, CFSTR("\tstatistics <service>\n"));
1181 SCPrint(TRUE, stderr, CFSTR("\t\tProvide statistics on bytes, packets, and errors for a given service\n"));
1182 SCPrint(TRUE, stderr, CFSTR("\n"));
1183 SCPrint(TRUE, stderr, CFSTR("\tselect <service>\n"));
1184 SCPrint(TRUE, stderr, CFSTR("\t\tMake the given service active in the current set. This allows it to be started\n"));
1185 SCPrint(TRUE, stderr, CFSTR("\n"));
1186 SCPrint(TRUE, stderr, CFSTR("\tstart <service> [--user user] [--password password] [--secret secret]\n"));
1187 SCPrint(TRUE, stderr, CFSTR("\t\tStart a given service. Can take optional arguments for user, password, and secret\n"));
1188 SCPrint(TRUE, stderr, CFSTR("\n"));
1189 SCPrint(TRUE, stderr, CFSTR("\tstop <service>\n"));
1190 SCPrint(TRUE, stderr, CFSTR("\t\tStop a given service\n"));
1191 SCPrint(TRUE, stderr, CFSTR("\n"));
1192 SCPrint(TRUE, stderr, CFSTR("\tsuspend <service>\n"));
1193 SCPrint(TRUE, stderr, CFSTR("\t\tSuspend a given service (PPP, Modem on Hold)\n"));
1194 SCPrint(TRUE, stderr, CFSTR("\n"));
1195 SCPrint(TRUE, stderr, CFSTR("\tresume <service>\n"));
1196 SCPrint(TRUE, stderr, CFSTR("\t\tResume a given service (PPP, Modem on Hold)\n"));
1197 SCPrint(TRUE, stderr, CFSTR("\n"));
1198 SCPrint(TRUE, stderr, CFSTR("\tondemand [-W] [hostname]\n"));
1199 SCPrint(TRUE, stderr, CFSTR("\tondemand -- --refresh\n"));
1200 SCPrint(TRUE, stderr, CFSTR("\t\tDisplay VPN on-demand information\n"));
1201 SCPrint(TRUE, stderr, CFSTR("\n"));
1202 SCPrint(TRUE, stderr, CFSTR("\ttrigger <hostname> [background] [port]\n"));
1203 SCPrint(TRUE, stderr, CFSTR("\t\tTrigger VPN on-demand with specified hostname, and optional port and background flag\n"));
1204 SCPrint(TRUE, stderr, CFSTR("\n"));
1205 #if !TARGET_OS_IPHONE
1206 SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type> [path]\n"));
1207 SCPrint(TRUE, stderr, CFSTR("\t\tEnables the given VPN application type. Takes either a service or VPN type. Pass a path to set ApplicationURL\n"));
1208 SCPrint(TRUE, stderr, CFSTR("\n"));
1209 #else // !TARGET_OS_IPHONE
1210 SCPrint(TRUE, stderr, CFSTR("\tenablevpn <service or vpn type>\n"));
1211 SCPrint(TRUE, stderr, CFSTR("\t\tEnables the given VPN application type. Takes either a service or VPN type\n"));
1212 SCPrint(TRUE, stderr, CFSTR("\n"));
1213 #endif // !TARGET_OS_IPHONE
1214 SCPrint(TRUE, stderr, CFSTR("\tdisablevpn <service or vpn type>\n"));
1215 SCPrint(TRUE, stderr, CFSTR("\t\tDisables the given VPN application type. Takes either a service or VPN type\n"));
1216 SCPrint(TRUE, stderr, CFSTR("\n"));
1217 SCPrint(TRUE, stderr, CFSTR("\thelp\n"));
1218 SCPrint(TRUE, stderr, CFSTR("\t\tDisplay available commands for --nc\n"));
1219 SCPrint(TRUE, stderr, CFSTR("\n"));
1220 exit(0);
1221 }
1222
1223 /* -----------------------------------------------------------------------------
1224 ----------------------------------------------------------------------------- */
1225 typedef void (*nc_func) (int argc, char **argv);
1226
1227 static const struct {
1228 char *cmd;
1229 nc_func func;
1230 } nc_cmds[] = {
1231 { "enablevpn", nc_enablevpn },
1232 { "help", nc_help },
1233 { "list", nc_list },
1234 { "ondemand", nc_ondemand },
1235 { "resume", nc_resume },
1236 { "select", nc_select },
1237 { "show", nc_show },
1238 { "start", nc_start },
1239 { "statistics", nc_statistics },
1240 { "status", nc_status },
1241 { "stop", nc_stop },
1242 { "suspend", nc_suspend },
1243 { "trigger", nc_trigger },
1244 };
1245 #define N_NC_CMNDS (sizeof(nc_cmds) / sizeof(nc_cmds[0]))
1246
1247
1248 /* -----------------------------------------------------------------------------
1249 ----------------------------------------------------------------------------- */
1250 int
1251 find_nc_cmd(char *cmd)
1252 {
1253 int i;
1254
1255 for (i = 0; i < (int)N_NC_CMNDS; i++) {
1256 if (strcmp(cmd, nc_cmds[i].cmd) == 0) {
1257 return i;
1258 }
1259 }
1260
1261 return -1;
1262 }
1263
1264
1265 /* -----------------------------------------------------------------------------
1266 ----------------------------------------------------------------------------- */
1267 void
1268 do_nc_cmd(char *cmd, int argc, char **argv, Boolean watch)
1269 {
1270 int i;
1271
1272 i = find_nc_cmd(cmd);
1273 if (i >= 0) {
1274 nc_func func;
1275
1276 func = nc_cmds[i].func;
1277 if (watch) {
1278 if (func == nc_status) {
1279 func = nc_watch;
1280 } else if (func == nc_ondemand) {
1281 ondemandwatch = TRUE;
1282 }
1283 }
1284 (*func)(argc, argv);
1285 }
1286 return;
1287 }
1288