2 * Copyright (c) 2010-2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 1, 2010 Christophe Allie <callie@apple.com>
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
46 CFStringRef username
= NULL
;
47 CFStringRef password
= NULL
;
48 CFStringRef sharedsecret
= NULL
;
50 static SCNetworkConnectionRef connection
= NULL
;
51 static int n_callback
= 0;
54 /* -----------------------------------------------------------------------------
55 ----------------------------------------------------------------------------- */
59 void * * obj
= (void * *)t
;
67 /* -----------------------------------------------------------------------------
68 ----------------------------------------------------------------------------- */
70 nc_get_service_type_and_subtype(SCNetworkServiceRef service
, CFStringRef
*iftype
, CFStringRef
*ifsubtype
) {
71 SCNetworkInterfaceRef interface
= SCNetworkServiceGetInterface(service
);
72 SCNetworkInterfaceRef child
= SCNetworkInterfaceGetInterface(interface
);
74 *iftype
= SCNetworkInterfaceGetInterfaceType(interface
);
76 if (CFEqual(*iftype
, kSCNetworkInterfaceTypePPP
) ||
77 CFEqual(*iftype
, kSCNetworkInterfaceTypeVPN
)) {
78 *ifsubtype
= (child
!= NULL
) ? SCNetworkInterfaceGetInterfaceType(child
) : NULL
;
82 /* -----------------------------------------------------------------------------
83 ----------------------------------------------------------------------------- */
84 static SCNetworkServiceRef
85 nc_copy_service(SCNetworkSetRef set
, CFStringRef identifier
)
89 SCNetworkServiceRef selected
= NULL
;
92 services
= SCNetworkConnectionCopyAvailableServices(set
);
93 if (services
== NULL
) {
97 n
= CFArrayGetCount(services
);
99 // try to select the service by its serviceID
100 for (i
= 0; i
< n
; i
++) {
101 SCNetworkServiceRef service
= NULL
;
102 CFStringRef serviceID
;
104 service
= CFArrayGetValueAtIndex(services
, i
);
105 serviceID
= SCNetworkServiceGetServiceID(service
);
106 if (CFEqual(identifier
, serviceID
)) {
112 // try to select the service by service name
113 for (i
= 0; i
< n
; i
++) {
114 SCNetworkServiceRef service
= NULL
;
115 CFStringRef serviceName
;
117 service
= CFArrayGetValueAtIndex(services
, i
);
118 serviceName
= SCNetworkServiceGetName(service
);
119 if ((serviceName
!= NULL
) && CFEqual(identifier
, serviceName
)) {
120 if (selected
== NULL
) {
123 // if multiple services match
125 SCPrint(TRUE
, stderr
, CFSTR("Multiple services match\n"));
133 if (selected
!= NULL
) CFRetain(selected
);
134 if (services
!= NULL
) CFRelease(services
);
138 /* -----------------------------------------------------------------------------
139 ----------------------------------------------------------------------------- */
140 static SCNetworkServiceRef
141 nc_copy_service_from_arguments(int argc
, char **argv
, SCNetworkSetRef set
) {
142 CFStringRef serviceID
= NULL
;
143 SCNetworkServiceRef service
= NULL
;
146 serviceID
= _copyStringFromSTDIN();
148 serviceID
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
150 if (serviceID
== NULL
) {
151 SCPrint(TRUE
, stderr
, CFSTR("No service ID specified\n"));
154 service
= nc_copy_service(set
, serviceID
);
155 my_CFRelease(&serviceID
);
160 /* -----------------------------------------------------------------------------
161 ----------------------------------------------------------------------------- */
163 nc_status_string(SCNetworkConnectionStatus status
)
166 case kSCNetworkConnectionInvalid
:
168 case kSCNetworkConnectionDisconnected
:
169 return "Disconnected";
170 case kSCNetworkConnectionConnecting
:
172 case kSCNetworkConnectionConnected
:
174 case kSCNetworkConnectionDisconnecting
:
175 return "Disconnecting";
181 nc_callback(SCNetworkConnectionRef connection
, SCNetworkConnectionStatus status
, void *info
)
183 int *n
= (int *)info
;
184 CFDictionaryRef status_dict
;
189 SCPrint(TRUE
, stdout
, CFSTR("Current status = "));
192 struct timeval tv_now
;
194 (void)gettimeofday(&tv_now
, NULL
);
195 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
197 SCPrint(TRUE
, stdout
, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
201 tv_now
.tv_usec
/ 1000);
202 SCPrint(TRUE
, stdout
, CFSTR("Callback (%d) status = "), *n
);
206 SCPrint(TRUE
, stdout
, CFSTR("%s%s%s\n"),
207 nc_status_string(status
),
208 (status
== kSCNetworkConnectionInvalid
) ? ": " : "",
209 (status
== kSCNetworkConnectionInvalid
) ? SCErrorString(SCError()) : "");
211 // report extended status
212 status_dict
= SCNetworkConnectionCopyExtendedStatus(connection
);
214 SCPrint(TRUE
, stdout
, CFSTR("Extended Status %@\n"), status_dict
);
215 CFRelease(status_dict
);
222 nc_create_connection(int argc
, char **argv
, Boolean exit_on_failure
)
224 SCNetworkConnectionContext context
= { 0, &n_callback
, NULL
, NULL
, NULL
};
225 SCNetworkServiceRef service
;
227 service
= nc_copy_service_from_arguments(argc
, argv
, NULL
);
228 if (service
== NULL
) {
229 SCPrint(TRUE
, stderr
, CFSTR("No service\n"));
235 connection
= SCNetworkConnectionCreateWithService(NULL
, service
, nc_callback
, &context
);
237 if (connection
== NULL
) {
238 SCPrint(TRUE
, stderr
, CFSTR("Could not create connection: %s\n"), SCErrorString(SCError()));
245 /* -----------------------------------------------------------------------------
246 ----------------------------------------------------------------------------- */
248 nc_release_connection()
250 my_CFRelease(&connection
);
253 /* -----------------------------------------------------------------------------
254 ----------------------------------------------------------------------------- */
256 nc_start(int argc
, char **argv
)
258 CFMutableDictionaryRef userOptions
= NULL
;
259 CFStringRef iftype
= NULL
;
260 CFStringRef ifsubtype
= NULL
;
261 SCNetworkServiceRef service
= NULL
;
263 nc_create_connection(argc
, argv
, TRUE
);
265 service
= SCNetworkConnectionGetService(connection
);
266 nc_get_service_type_and_subtype(service
, &iftype
, &ifsubtype
);
268 userOptions
= CFDictionaryCreateMutable(NULL
, 0,
269 &kCFTypeDictionaryKeyCallBacks
,
270 &kCFTypeDictionaryValueCallBacks
);
272 Boolean isL2TP
= (CFEqual(iftype
, kSCEntNetPPP
) &&
273 (ifsubtype
!= NULL
) && CFEqual(ifsubtype
, kSCValNetInterfaceSubTypeL2TP
));
275 if (CFEqual(iftype
, kSCEntNetPPP
)) {
276 CFMutableDictionaryRef pppEntity
= CFDictionaryCreateMutable(NULL
, 0,
277 &kCFTypeDictionaryKeyCallBacks
,
278 &kCFTypeDictionaryValueCallBacks
);
280 if (username
!= NULL
) {
281 CFDictionarySetValue(pppEntity
, kSCPropNetPPPAuthName
, username
);
283 if (password
!= NULL
) {
284 CFDictionarySetValue(pppEntity
, kSCPropNetPPPAuthPassword
, password
);
286 CFDictionarySetValue(userOptions
, kSCEntNetPPP
, pppEntity
);
287 my_CFRelease(&pppEntity
);
289 if (CFEqual(iftype
, kSCEntNetIPSec
) || isL2TP
) {
290 CFMutableDictionaryRef ipsecEntity
= CFDictionaryCreateMutable(NULL
, 0,
291 &kCFTypeDictionaryKeyCallBacks
,
292 &kCFTypeDictionaryValueCallBacks
);
294 if (username
!= NULL
) {
295 CFDictionarySetValue(ipsecEntity
, kSCPropNetIPSecXAuthName
, username
);
297 if (password
!= NULL
) {
298 CFDictionarySetValue(ipsecEntity
, kSCPropNetIPSecXAuthPassword
, password
);
301 if (sharedsecret
!= NULL
) {
302 CFDictionarySetValue(ipsecEntity
, kSCPropNetIPSecSharedSecret
, sharedsecret
);
304 CFDictionarySetValue(userOptions
, kSCEntNetIPSec
, ipsecEntity
);
305 my_CFRelease(&ipsecEntity
);
307 if (CFEqual(iftype
, kSCEntNetVPN
)) {
308 CFMutableDictionaryRef vpnEntity
= CFDictionaryCreateMutable(NULL
, 0,
309 &kCFTypeDictionaryKeyCallBacks
,
310 &kCFTypeDictionaryValueCallBacks
);
311 if (username
!= NULL
) {
312 CFDictionarySetValue(vpnEntity
, kSCPropNetVPNAuthName
, username
);
314 if (password
!= NULL
) {
315 CFDictionarySetValue(vpnEntity
, kSCPropNetVPNAuthPassword
, password
);
317 CFDictionarySetValue(userOptions
, kSCEntNetVPN
, vpnEntity
);
318 my_CFRelease(&vpnEntity
);
320 // If it doesn't match any VPN type, fail silently
322 if (!SCNetworkConnectionStart(connection
, userOptions
, TRUE
)) {
323 SCPrint(TRUE
, stderr
, CFSTR("Could not start connection: %s\n"), SCErrorString(SCError()));
327 CFRelease(userOptions
);
328 nc_release_connection();
332 /* -----------------------------------------------------------------------------
333 ----------------------------------------------------------------------------- */
335 nc_stop(int argc
, char **argv
)
337 nc_create_connection(argc
, argv
, TRUE
);
339 if (!SCNetworkConnectionStop(connection
, TRUE
)) {
340 SCPrint(TRUE
, stderr
, CFSTR("Could not stop connection: %s\n"), SCErrorString(SCError()));
344 nc_release_connection();
348 /* -----------------------------------------------------------------------------
349 ----------------------------------------------------------------------------- */
351 nc_suspend(int argc
, char **argv
)
353 nc_create_connection(argc
, argv
, TRUE
);
355 SCNetworkConnectionSuspend(connection
);
357 nc_release_connection();
361 /* -----------------------------------------------------------------------------
362 ----------------------------------------------------------------------------- */
364 nc_resume(int argc
, char **argv
)
366 nc_create_connection(argc
, argv
, TRUE
);
368 SCNetworkConnectionResume(connection
);
370 nc_release_connection();
374 /* -----------------------------------------------------------------------------
375 ----------------------------------------------------------------------------- */
377 nc_status(int argc
, char **argv
)
379 SCNetworkConnectionStatus status
;
381 nc_create_connection(argc
, argv
, TRUE
);
383 status
= SCNetworkConnectionGetStatus(connection
);
384 nc_callback(connection
, status
, NULL
);
386 nc_release_connection();
391 nc_watch(int argc
, char **argv
)
393 SCNetworkConnectionStatus status
;
395 nc_create_connection(argc
, argv
, TRUE
);
397 status
= SCNetworkConnectionGetStatus(connection
);
399 // report initial status
401 nc_callback(connection
, status
, &n_callback
);
405 if (!SCNetworkConnectionSetDispatchQueue(connection
, dispatch_get_current_queue())) {
406 SCPrint(TRUE
, stderr
, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
410 if (!SCNetworkConnectionScheduleWithRunLoop(connection
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) {
411 SCPrint(TRUE
, stderr
, CFSTR("Unable to schedule watch process: %s\n"), SCErrorString(SCError()));
419 nc_release_connection();
423 /* -----------------------------------------------------------------------------
424 ----------------------------------------------------------------------------- */
426 nc_statistics(int argc
, char **argv
)
428 CFDictionaryRef stats_dict
;
430 nc_create_connection(argc
, argv
, TRUE
);
432 stats_dict
= SCNetworkConnectionCopyStatistics(connection
);
435 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), stats_dict
);
437 SCPrint(TRUE
, stdout
, CFSTR("No statistics available\n"));
440 my_CFRelease(&stats_dict
);
442 nc_release_connection();
446 /* -----------------------------------------------------------------------------
447 ----------------------------------------------------------------------------- */
449 checkOnDemandHost(SCDynamicStoreRef store
, CFStringRef nodeName
, Boolean retry
)
452 CFStringRef connectionServiceID
= NULL
;
453 SCNetworkConnectionStatus connectionStatus
= 0;
454 CFStringRef vpnRemoteAddress
= NULL
;
456 SCPrint(TRUE
, stdout
, CFSTR("OnDemand host/domain check (%sretry)\n"), retry
? "" : "no ");
458 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
,
461 &connectionServiceID
,
466 SCPrint(TRUE
, stdout
, CFSTR(" serviceID = %@\n"), connectionServiceID
);
467 SCPrint(TRUE
, stdout
, CFSTR(" remote address = %@\n"), vpnRemoteAddress
);
468 } else if (SCError() != kSCStatusOK
) {
469 SCPrint(TRUE
, stdout
, CFSTR("%sretry\n"), retry
? "" : "no ");
470 SCPrint(TRUE
, stdout
,
471 CFSTR(" Unable to copy OnDemand information for connection: %s\n"),
472 SCErrorString(SCError()));
474 SCPrint(TRUE
, stdout
, CFSTR(" no match\n"));
477 if (connectionServiceID
!= NULL
) {
478 CFRelease(connectionServiceID
);
479 connectionServiceID
= NULL
;
481 if (vpnRemoteAddress
!= NULL
) {
482 CFRelease(vpnRemoteAddress
);
483 vpnRemoteAddress
= NULL
;
490 nc_ondemand(int argc
, char **argv
)
493 CFStringRef key
= NULL
;
494 CFDictionaryRef ondemand_dict
= NULL
;
495 SCDynamicStoreRef store
;
497 store
= SCDynamicStoreCreate(NULL
, CFSTR("scutil --nc"), NULL
, NULL
);
499 SCPrint(TRUE
, stderr
, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
504 CFStringRef nodeName
;
506 nodeName
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
507 checkOnDemandHost(store
, nodeName
, FALSE
);
508 checkOnDemandHost(store
, nodeName
, TRUE
);
512 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, kSCDynamicStoreDomainState
, kSCEntNetOnDemand
);
514 ondemand_dict
= SCDynamicStoreCopyValue(store
, key
);
516 SCPrint(TRUE
, stdout
, CFSTR("%@ %@\n"), kSCEntNetOnDemand
, ondemand_dict
);
518 SCPrint(TRUE
, stdout
, CFSTR("%@ not configured\n"), kSCEntNetOnDemand
);
523 my_CFRelease(&ondemand_dict
);
525 my_CFRelease(&store
);
530 /* -----------------------------------------------------------------------------
531 ----------------------------------------------------------------------------- */
532 #define MAX(a, b) ((a) > (b) ? (a) : (b))
535 copy_padded_string(CFStringRef original
, int width
)
537 CFMutableStringRef padded
;
539 padded
= CFStringCreateMutableCopy(NULL
, 0, original
);
540 CFStringPad(padded
, CFSTR(" "), MAX(CFStringGetLength(original
), width
), 0);
546 nc_print_VPN_service(SCNetworkServiceRef service
)
548 CFStringRef type
= NULL
;
549 CFStringRef sub_type
= NULL
;
551 nc_get_service_type_and_subtype(service
, &type
, &sub_type
);
553 CFStringRef service_name
= SCNetworkServiceGetName(service
);
554 if (service_name
== NULL
)
555 service_name
= CFSTR("");
556 CFStringRef service_name_quoted
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\"%@\""), service_name
);
557 if (service_name_quoted
== NULL
) {
558 service_name_quoted
= CFRetain(CFSTR(""));
560 CFStringRef service_name_padded
= copy_padded_string(service_name
, 30);
562 CFStringRef service_id
= SCNetworkServiceGetServiceID(service
);
563 SCNetworkInterfaceRef interface
= SCNetworkServiceGetInterface(service
);
564 CFStringRef display_name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
565 if (display_name
== NULL
)
566 display_name
= CFSTR("");
567 CFStringRef display_name_padded
= copy_padded_string(display_name
, 18);
572 CFSTR("%@ %@ %@ %@ [%@%@%@]\n"),
573 SCNetworkServiceGetEnabled(service
) ? CFSTR("*") : CFSTR(" "),
578 (sub_type
== NULL
) ? CFSTR("") : CFSTR(":"),
579 (sub_type
== NULL
) ? CFSTR("") : sub_type
);
580 CFRelease(service_name_quoted
);
581 CFRelease(display_name_padded
);
582 CFRelease(service_name_padded
);
586 /* -----------------------------------------------------------------------------
587 ----------------------------------------------------------------------------- */
589 nc_list(int argc
, char **argv
)
593 CFArrayRef services
= NULL
;
595 SCPrint(TRUE
, stdout
, CFSTR("Available network connection services in the current set (*=enabled):\n"));
596 services
= SCNetworkConnectionCopyAvailableServices(NULL
);
597 if (services
!= NULL
) {
598 count
= CFArrayGetCount(services
);
600 for (i
= 0; i
< count
; i
++) {
601 SCNetworkServiceRef service
;
603 service
= CFArrayGetValueAtIndex(services
, i
);
604 nc_print_VPN_service(service
);
608 my_CFRelease(&services
);
613 /* -----------------------------------------------------------------------------
614 ----------------------------------------------------------------------------- */
616 nc_show(int argc
, char **argv
)
618 SCNetworkServiceRef service
= NULL
;
619 SCDynamicStoreRef store
= NULL
;
621 CFStringRef serviceID
= NULL
;
622 CFStringRef iftype
= NULL
;
623 CFStringRef ifsubtype
= NULL
;
624 CFStringRef type_entity_key
= NULL
;
625 CFStringRef subtype_entity_key
= NULL
;
626 CFDictionaryRef type_entity_dict
= NULL
;
627 CFDictionaryRef subtype_entity_dict
= NULL
;
629 service
= nc_copy_service_from_arguments(argc
, argv
, NULL
);
630 if (service
== NULL
) {
631 SCPrint(TRUE
, stderr
, CFSTR("No service\n"));
635 serviceID
= SCNetworkServiceGetServiceID(service
);
637 nc_get_service_type_and_subtype(service
, &iftype
, &ifsubtype
);
639 if (!CFEqual(iftype
, kSCEntNetPPP
) &&
640 !CFEqual(iftype
, kSCEntNetIPSec
) &&
641 !CFEqual(iftype
, kSCEntNetVPN
)) {
642 SCPrint(TRUE
, stderr
, CFSTR("Not a connection oriented service: %@\n"), serviceID
);
646 type_entity_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainSetup
, serviceID
, iftype
);
648 nc_print_VPN_service(service
);
650 store
= SCDynamicStoreCreate(NULL
, CFSTR("scutil --nc"), NULL
, NULL
);
652 SCPrint(TRUE
, stderr
, CFSTR("Unable to create dynamic store: %s\n"), SCErrorString(SCError()));
655 type_entity_dict
= SCDynamicStoreCopyValue(store
, type_entity_key
);
657 if (!type_entity_dict
) {
658 SCPrint(TRUE
, stderr
, CFSTR("No \"%@\" configuration available\n"), iftype
);
660 SCPrint(TRUE
, stdout
, CFSTR("%@ %@\n"), iftype
, type_entity_dict
);
664 subtype_entity_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, kSCDynamicStoreDomainSetup
, serviceID
, ifsubtype
);
665 subtype_entity_dict
= SCDynamicStoreCopyValue(store
, subtype_entity_key
);
666 if (!subtype_entity_dict
) {
670 SCPrint(TRUE
, stdout
, CFSTR("%@ %@\n"), ifsubtype
, subtype_entity_dict
);
677 my_CFRelease(&type_entity_key
);
678 my_CFRelease(&type_entity_dict
);
679 my_CFRelease(&subtype_entity_key
);
680 my_CFRelease(&subtype_entity_dict
);
681 my_CFRelease(&store
);
682 my_CFRelease(&service
);
686 /* -----------------------------------------------------------------------------
687 ----------------------------------------------------------------------------- */
689 nc_select(int argc
, char **argv
)
691 SCNetworkSetRef current_set
;
693 SCNetworkServiceRef service
= NULL
;
696 do_prefs_init(); /* initialization */
697 do_prefs_open(0, NULL
); /* open default prefs */
699 current_set
= SCNetworkSetCopyCurrent(prefs
);
700 if (current_set
== NULL
) {
701 SCPrint(TRUE
, stderr
, CFSTR("No current location\n"), SCErrorString(SCError()));
705 service
= nc_copy_service_from_arguments(argc
, argv
, current_set
);
706 if (service
== NULL
) {
707 SCPrint(TRUE
, stderr
, CFSTR("No service\n"));
711 #if !TARGET_OS_IPHONE
712 status
= SCNetworkServiceSetEnabled(service
, TRUE
);
714 SCPrint(TRUE
, stderr
, CFSTR("Unable to enable service: %s\n"), SCErrorString(SCError()));
718 status
= SCNetworkSetSetSelectedVPNService(current_set
, service
);
720 SCPrint(TRUE
, stderr
, CFSTR("Unable to select service: %s\n"), SCErrorString(SCError()));
728 my_CFRelease(&service
);
729 my_CFRelease(¤t_set
);
734 /* -----------------------------------------------------------------------------
735 ----------------------------------------------------------------------------- */
736 typedef void (*nc_func
) (int argc
, char **argv
);
738 static const struct {
743 { "ondemand", nc_ondemand
},
744 { "resume", nc_resume
},
745 { "select", nc_select
},
747 { "start", nc_start
},
748 { "statistics", nc_statistics
},
749 { "status", nc_status
},
751 { "suspend", nc_suspend
},
753 #define N_NC_CMNDS (sizeof(nc_cmds) / sizeof(nc_cmds[0]))
756 /* -----------------------------------------------------------------------------
757 ----------------------------------------------------------------------------- */
759 find_nc_cmd(char *cmd
)
763 for (i
= 0; i
< (int)N_NC_CMNDS
; i
++) {
764 if (strcmp(cmd
, nc_cmds
[i
].cmd
) == 0) {
773 /* -----------------------------------------------------------------------------
774 ----------------------------------------------------------------------------- */
776 do_nc_cmd(char *cmd
, int argc
, char **argv
, Boolean watch
)
780 i
= find_nc_cmd(cmd
);
784 func
= nc_cmds
[i
].func
;
785 if (watch
&& (func
== nc_status
)) {