2 * Copyright (c) 2000-2020 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 * June 13, 2005 Allan Nathanson <ajn@apple.com>
28 * - added SCPreferences support
30 * August 4, 2004 Allan Nathanson <ajn@apple.com>
31 * - added network configuration (prefs) support
33 * September 25, 2002 Allan Nathanson <ajn@apple.com>
34 * - added command line history & editing
36 * July 9, 2001 Allan Nathanson <ajn@apple.com>
37 * - added "-r" option for checking network reachability
38 * - added "-w" option to check/wait for the presence of a
41 * June 1, 2001 Allan Nathanson <ajn@apple.com>
42 * - public API conversion
44 * November 9, 2000 Allan Nathanson <ajn@apple.com>
48 #include <TargetConditionals.h>
59 #include <mach/mach.h>
60 #include <mach/mach_error.h>
65 #include "dictionary.h"
72 #define LINE_LENGTH 2048
74 __private_extern__ AuthorizationRef authorization
= NULL
;
75 __private_extern__ InputRef currentInput
= NULL
;
76 __private_extern__ Boolean doDispatch
= FALSE
;
77 __private_extern__
int nesting
= 0;
78 __private_extern__ SCPreferencesRef ni_prefs
= NULL
;
79 __private_extern__ CFRunLoopRef notifyRl
= NULL
;
80 __private_extern__ CFRunLoopSourceRef notifyRls
= NULL
;
81 __private_extern__ SCPreferencesRef prefs
= NULL
;
82 __private_extern__
char *prefsPath
= NULL
;
83 __private_extern__ SCDynamicStoreRef store
= NULL
;
84 __private_extern__ CFPropertyListRef value
= NULL
;
85 __private_extern__ CFMutableArrayRef watchedKeys
= NULL
;
86 __private_extern__ CFMutableArrayRef watchedPatterns
= NULL
;
88 static const struct option longopts
[] = {
89 // { "debug", no_argument, NULL, 'd' },
90 // { "dispatch", no_argument, NULL, 'D' },
91 // { "verbose", no_argument, NULL, 'v' },
92 // { "SPI", no_argument, NULL, 'p' },
93 // { "check-reachability", required_argument, NULL, 'r' },
94 // { "timeout", required_argument, NULL, 't' },
95 // { "wait-key", required_argument, NULL, 'w' },
96 // { "watch-reachability", no_argument, NULL, 'W' },
97 { "configuration", no_argument
, NULL
, 0 },
98 { "dns", no_argument
, NULL
, 0 },
99 { "get", required_argument
, NULL
, 0 },
100 { "error", required_argument
, NULL
, 0 },
101 { "help", no_argument
, NULL
, '?' },
102 { "nc", required_argument
, NULL
, 0 },
103 { "net", no_argument
, NULL
, 0 },
104 { "nwi", no_argument
, NULL
, 0 },
105 { "prefs", no_argument
, NULL
, 0 },
106 { "proxy", no_argument
, NULL
, 0 },
107 { "renew", required_argument
, NULL
, 0 },
108 { "set", required_argument
, NULL
, 0 },
109 { "snapshot", no_argument
, NULL
, 0 },
110 { "user", required_argument
, NULL
, 0 },
111 { "password", required_argument
, NULL
, 0 },
112 { "secret", required_argument
, NULL
, 0 },
113 { "log", required_argument
, NULL
, 0 },
114 { "advisory", required_argument
, NULL
, 0 },
115 #if !TARGET_OS_IPHONE
116 { "allow-new-interfaces", no_argument
, NULL
, 0 },
117 #endif // !TARGET_OS_IPHONE
118 { "disable-until-needed", no_argument
, NULL
, 0 },
125 _copyStringFromSTDIN(CFStringRef prompt
, CFStringRef defaultValue
)
129 Boolean is_user_prompt
= (prompt
!= NULL
&& isatty(STDIN_FILENO
) && isatty(STDOUT_FILENO
));
135 /* Print out a prompt to user that entry is desired */
136 if (is_user_prompt
) {
137 if (defaultValue
!= NULL
) {
138 SCPrint(TRUE
, stdout
, CFSTR("%@ [%@]: "), prompt
, defaultValue
);
140 SCPrint(TRUE
, stdout
, CFSTR("%@: "), prompt
);
145 if (fgets(buf
, sizeof(buf
), stdin
) == NULL
) {
149 /* Prepare for trim */
150 len
= (int)strlen(buf
);
155 if ((modlen
> 0) && (modbuf
[modlen
- 1] == '\n')) {
156 modbuf
[modlen
- 1] = '\0';
160 /* If nothing was entered at the user prompt, set default */
161 if (is_user_prompt
&& defaultValue
!= NULL
&& modlen
== 0) {
162 CFRetain(defaultValue
);
166 /* Trim spaces from front */
167 while (modlen
> 0 && isspace(modbuf
[0])) {
172 /* Trim spaces from back */
173 for (i
= modlen
- 1; i
>= 0; i
--) {
174 if (isspace(buf
[i
])) {
182 utf8
= CFStringCreateWithBytes(NULL
, (UInt8
*)modbuf
, modlen
, kCFStringEncodingUTF8
, TRUE
);
187 getLine(char *buf
, int len
, InputRef src
)
195 line
= el_gets(src
->el
, &count
);
199 strlcpy(buf
, line
, len
);
201 if (fgets(buf
, len
, src
->fp
) == NULL
)
205 n
= (int)strlen(buf
);
206 if (buf
[n
-1] == '\n') {
207 /* the entire line fit in the buffer, remove the newline */
209 } else if (!src
->el
) {
210 /* eat the remainder of the line */
213 } while ((n
!= '\n') && (n
!= EOF
));
216 if (src
->h
&& (buf
[0] != '\0')) {
219 history(src
->h
, &ev
, H_ENTER
, buf
);
227 getString(char **line
)
229 char *s
, *e
, c
, *string
;
230 int i
, isQuoted
= 0, escaped
= 0;
232 if (*line
== NULL
) return NULL
;
233 if (**line
== '\0') return NULL
;
235 /* Skip leading white space */
236 while (isspace(**line
)) *line
+= 1;
238 /* Grab the next string */
241 return NULL
; /* no string available */
242 } else if (*s
== '"') {
243 isQuoted
= 1; /* it's a quoted string */
247 for (e
= s
; (c
= *e
) != '\0'; e
++) {
248 if (isQuoted
&& (c
== '"'))
249 break; /* end of quoted string */
253 break; /* if premature end-of-string */
254 if ((*e
== '"') || isspace(*e
))
255 escaped
++; /* if escaped quote or white space */
257 if (!isQuoted
&& isspace(c
))
258 break; /* end of non-quoted string */
261 string
= malloc(e
- s
- escaped
+ 1);
263 for (i
= 0; s
< e
; s
++) {
265 if (!((s
[0] == '\\') && ((s
[1] == '"') || isspace(s
[1])))) i
++;
270 e
++; /* move past end of quoted string */
279 process_line(InputRef src
)
285 char line
[LINE_LENGTH
];
288 // if end-of-file, exit
289 if (getLine(line
, sizeof(line
), src
) == NULL
)
293 SCPrint(TRUE
, stdout
, CFSTR("%d> %s\n"), nesting
, line
);
296 // break up the input line
297 while ((arg
= getString(&s
)) != NULL
) {
299 argv
= (char **)malloc(2 * sizeof(char *));
301 argv
= (char **)reallocf(argv
, ((argc
+ 2) * sizeof(char *)));
306 return TRUE
; // if no arguments
309 /* process the command */
310 if (*argv
[0] != '#') {
311 argv
[argc
] = NULL
; // just in case...
313 do_command(argc
, argv
);
316 /* free the arguments */
317 for (i
= 0; i
< argc
; i
++) {
322 return !termRequested
;
327 usage(const char *command
)
329 SCPrint(TRUE
, stderr
, CFSTR("usage: %s\n"), command
);
330 SCPrint(TRUE
, stderr
, CFSTR("\tinteractive access to the dynamic store.\n"));
331 SCPrint(TRUE
, stderr
, CFSTR("\n"));
332 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --prefs [preference-file]\n"), command
);
333 SCPrint(TRUE
, stderr
, CFSTR("\tinteractive access to the [raw] stored preferences.\n"));
334 SCPrint(TRUE
, stderr
, CFSTR("\n"));
335 SCPrint(TRUE
, stderr
, CFSTR(" or: %s [-W] -r nodename\n"), command
);
336 SCPrint(TRUE
, stderr
, CFSTR(" or: %s [-W] -r address\n"), command
);
337 SCPrint(TRUE
, stderr
, CFSTR(" or: %s [-W] -r local-address remote-address\n"), command
);
338 SCPrint(TRUE
, stderr
, CFSTR("\tcheck reachability of node, address, or address pair (-W to \"watch\").\n"));
339 SCPrint(TRUE
, stderr
, CFSTR("\n"));
340 SCPrint(TRUE
, stderr
, CFSTR(" or: %s -w dynamic-store-key [ -t timeout ]\n"), command
);
341 SCPrint(TRUE
, stderr
, CFSTR("\t-w\twait for presense of dynamic store key\n"));
342 SCPrint(TRUE
, stderr
, CFSTR("\t-t\ttime to wait for key\n"));
343 SCPrint(TRUE
, stderr
, CFSTR("\n"));
344 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --get pref\n"), command
);
345 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --set pref [newval]\n"), command
);
346 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --get filename path key \n"), command
);
347 SCPrint(TRUE
, stderr
, CFSTR("\tpref\tdisplay (or set) the specified preference. Valid preferences\n"));
348 SCPrint(TRUE
, stderr
, CFSTR("\t\tinclude:\n"));
349 SCPrint(TRUE
, stderr
, CFSTR("\t\t\tComputerName, LocalHostName, HostName\n"));
350 SCPrint(TRUE
, stderr
, CFSTR("\tnewval\tNew preference value to be set. If not specified,\n"));
351 SCPrint(TRUE
, stderr
, CFSTR("\t\tthe new value will be read from standard input.\n"));
352 SCPrint(TRUE
, stderr
, CFSTR("\n"));
353 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --dns\n"), command
);
354 SCPrint(TRUE
, stderr
, CFSTR("\tshow DNS configuration.\n"));
355 SCPrint(TRUE
, stderr
, CFSTR("\n"));
356 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --proxy\n"), command
);
357 SCPrint(TRUE
, stderr
, CFSTR("\tshow \"proxy\" configuration.\n"));
358 SCPrint(TRUE
, stderr
, CFSTR("\n"));
359 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --nwi\n"), command
);
360 SCPrint(TRUE
, stderr
, CFSTR("\tshow network information\n"));
361 SCPrint(TRUE
, stderr
, CFSTR("\n"));
362 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --nc\n"), command
);
363 SCPrint(TRUE
, stderr
, CFSTR("\tshow VPN network configuration information. Use --nc help for full command list\n"));
366 SCPrint(TRUE
, stderr
, CFSTR("\n"));
367 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --log IPMonitor [off|on]\n"), command
);
368 SCPrint(TRUE
, stderr
, CFSTR("\tmanage logging.\n"));
370 SCPrint(TRUE
, stderr
, CFSTR("\n"));
371 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --disable-until-needed <interfaceName> [on|off ]\n"), command
);
372 SCPrint(TRUE
, stderr
, CFSTR("\tmanage secondary interface demand.\n"));
375 #if !TARGET_OS_IPHONE
376 SCPrint(TRUE
, stderr
, CFSTR("\n"));
377 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --allow-new-interfaces [off|on]\n"), command
);
378 SCPrint(TRUE
, stderr
, CFSTR("\tmanage new interface creation with screen locked.\n"));
379 #endif // !TARGET_OS_IPHONE
381 if (getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
382 SCPrint(TRUE
, stderr
, CFSTR("\n"));
383 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --net\n"), command
);
384 SCPrint(TRUE
, stderr
, CFSTR("\tmanage network configuration.\n"));
387 SCPrint(TRUE
, stderr
, CFSTR("\n"));
388 SCPrint(TRUE
, stderr
, CFSTR(" or: %s --error err#\n"), command
);
389 SCPrint(TRUE
, stderr
, CFSTR("\tdisplay a descriptive message for the given error code\n"));
399 #if !TARGET_OS_SIMULATOR
401 #else // !TARGET_OS_SIMULATOR
403 #endif // !TARGET_OS_SIMULATOR
408 main(int argc
, char * const argv
[])
410 const char * advisoryInterface
= NULL
;
411 #if !TARGET_OS_IPHONE
412 Boolean allowNewInterfaces
= FALSE
;
413 #endif // !TARGET_OS_IPHONE
414 Boolean configuration
= FALSE
;
415 Boolean disableUntilNeeded
= FALSE
;
416 Boolean doAdvisory
= FALSE
;
417 Boolean doDNS
= FALSE
;
418 Boolean doNet
= FALSE
;
419 Boolean doNWI
= FALSE
;
420 Boolean doPrefs
= FALSE
;
421 Boolean doProxy
= FALSE
;
422 Boolean doReach
= FALSE
;
423 Boolean doSnap
= FALSE
;
430 const char *prog
= argv
[0];
435 int timeout
= 15; /* default timeout (in seconds) */
437 Boolean watch
= FALSE
;
438 int xStore
= 0; /* non dynamic store command line options */
440 /* process any arguments */
442 while ((opt
= getopt_long(argc
, argv
, "dDvprt:w:W", longopts
, &opti
)) != -1) {
446 _sc_log
= kSCLogDestinationFile
; /* enable framework logging */
453 _sc_log
= kSCLogDestinationFile
; /* enable framework logging */
456 enablePrivateAPI
= TRUE
;
463 timeout
= atoi(optarg
);
473 if (strcmp(longopts
[opti
].name
, "configuration") == 0) {
474 configuration
= TRUE
;
476 } else if (strcmp(longopts
[opti
].name
, "dns") == 0) {
479 } else if (strcmp(longopts
[opti
].name
, "error") == 0) {
482 } else if (strcmp(longopts
[opti
].name
, "get") == 0) {
485 } else if (strcmp(longopts
[opti
].name
, "nc") == 0) {
488 } else if (strcmp(longopts
[opti
].name
, "net") == 0) {
491 } else if (strcmp(longopts
[opti
].name
, "nwi") == 0) {
494 } else if (strcmp(longopts
[opti
].name
, "prefs") == 0) {
497 } else if (strcmp(longopts
[opti
].name
, "proxy") == 0) {
500 } else if (strcmp(longopts
[opti
].name
, "renew") == 0) {
503 } else if (strcmp(longopts
[opti
].name
, "set") == 0) {
506 } else if (strcmp(longopts
[opti
].name
, "snapshot") == 0) {
509 } else if (strcmp(longopts
[opti
].name
, "log") == 0) {
512 #if !TARGET_OS_IPHONE
513 } else if (strcmp(longopts
[opti
].name
, "allow-new-interfaces") == 0) {
514 allowNewInterfaces
= TRUE
;
516 #endif // !TARGET_OS_IPHONE
517 } else if (strcmp(longopts
[opti
].name
, "disable-until-needed") == 0) {
518 disableUntilNeeded
= TRUE
;
520 } else if (strcmp(longopts
[opti
].name
, "user") == 0) {
521 username
= CFStringCreateWithCString(NULL
, optarg
, kCFStringEncodingUTF8
);
522 } else if (strcmp(longopts
[opti
].name
, "password") == 0) {
523 password
= CFStringCreateWithCString(NULL
, optarg
, kCFStringEncodingUTF8
);
524 } else if (strcmp(longopts
[opti
].name
, "secret") == 0) {
525 sharedsecret
= CFStringCreateWithCString(NULL
, optarg
, kCFStringEncodingUTF8
);
526 } else if (strcmp(longopts
[opti
].name
, "advisory") == 0) {
528 advisoryInterface
= optarg
;
542 // if we are attempting to process more than one type of request
546 /* are we asking for a configuration summary */
548 do_configuration(argc
, (char **)argv
);
552 /* are we checking (or watching) the reachability of a host/address */
558 do_watchReachability(argc
, (char **)argv
);
560 do_checkReachability(argc
, (char **)argv
);
565 /* are we waiting on the presense of a dynamic store key */
567 do_wait(wait
, timeout
);
571 /* are we looking up the DNS configuration */
574 do_watchDNSConfiguration(argc
, (char **)argv
);
576 do_showDNSConfiguration(argc
, (char **)argv
);
583 do_watchNWI(argc
, (char**)argv
);
585 do_showNWI(argc
, (char**)argv
);
591 if (!enablePrivateAPI
) {
595 do_open(0, NULL
); /* open the dynamic store */
596 do_snapshot(argc
, (char**)argv
);
601 do_advisory(advisoryInterface
, watch
, argc
, (char**)argv
);
605 /* are we translating error #'s to descriptive text */
607 int sc_status
= atoi(error
);
609 SCPrint(TRUE
, stdout
, CFSTR("Error: 0x%08x %d %s\n"),
612 SCErrorString(sc_status
));
616 /* are we looking up a preference value */
619 if (findPref(get
) < 0) {
622 } else if (argc
== 2) {
625 * i.e. scutil --get <filename> <prefs path> <key>
627 * need to go back one argument to re-use the 1st "--get"
628 * argument as the prefs path name
636 do_getPref(get
, argc
, (char **)argv
);
640 /* are we looking up the proxy configuration */
642 do_showProxyConfiguration(argc
, (char **)argv
);
646 /* are we changing a preference value */
648 if (findPref(set
) < 0) {
651 do_setPref(set
, argc
, (char **)argv
);
657 if (strcasecmp(log
, "IPMonitor")) {
660 do_log(log
, argc
, (char * *)argv
);
664 #if !TARGET_OS_IPHONE
665 /* allowNewInterfaces */
666 if (allowNewInterfaces
) {
667 do_ifnamer("allow-new-interfaces", argc
, (char * *)argv
);
670 #endif // !TARGET_OS_IPHONE
672 /* disableUntilNeeded */
673 if (disableUntilNeeded
) {
674 do_disable_until_needed(argc
, (char * *)argv
);
678 /* network connection commands */
680 if (find_nc_cmd(nc_cmd
) < 0) {
683 do_nc_cmd(nc_cmd
, argc
, (char **)argv
, watch
);
688 /* if we are going to be managing the network configuration */
689 commands
= (cmdInfo
*)commands_net
;
690 nCommands
= nCommands_net
;
692 if (!getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
696 do_net_init(); /* initialization */
697 do_net_open(argc
, (char **)argv
); /* open prefs */
698 } else if (doPrefs
) {
699 /* if we are going to be managing the network configuration */
700 commands
= (cmdInfo
*)commands_prefs
;
701 nCommands
= nCommands_prefs
;
703 do_dictInit(0, NULL
); /* start with an empty dictionary */
704 do_prefs_init(); /* initialization */
705 do_prefs_open(argc
, (char **)argv
); /* open prefs */
707 /* if we are going to be managing the dynamic store */
708 commands
= (cmdInfo
*)commands_store
;
709 nCommands
= nCommands_store
;
711 do_dictInit(0, NULL
); /* start with an empty dictionary */
712 do_open(0, NULL
); /* open the dynamic store */
715 /* are we trying to renew a DHCP lease */
721 /* allocate command input stream */
722 src
= (InputRef
)CFAllocatorAllocate(NULL
, sizeof(Input
), 0);
727 if (isatty(fileno(src
->fp
))) {
732 if (tcgetattr(fileno(src
->fp
), &t
) != -1) {
733 if ((t
.c_lflag
& ECHO
) == 0) {
737 src
->el
= el_init(prog
, src
->fp
, stdout
, stderr
);
738 src
->h
= history_init();
740 (void)history(src
->h
, &ev
, H_SETSIZE
, INT_MAX
);
741 el_set(src
->el
, EL_HIST
, history
, src
->h
);
744 el_set(src
->el
, EL_EDITMODE
, 0);
747 el_set(src
->el
, EL_EDITOR
, "emacs");
748 el_set(src
->el
, EL_PROMPT
, prompt
);
750 el_source(src
->el
, NULL
);
752 if ((el_get(src
->el
, EL_EDITMODE
, &editmode
) != -1) && editmode
!= 0) {
753 el_set(src
->el
, EL_SIGNAL
, 1);
765 ok
= process_line(src
);
771 /* close the socket, free resources */
772 if (src
->h
) history_end(src
->h
);
773 if (src
->el
) el_end(src
->el
);
774 (void)fclose(src
->fp
);
775 CFAllocatorDeallocate(NULL
, src
);
777 exit (EX_OK
); // insure the process exit status is 0
778 return 0; // ...and make main fit the ANSI spec.