]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/scutil.c
configd-453.19.tar.gz
[apple/configd.git] / scutil.tproj / scutil.c
1 /*
2 * Copyright (c) 2000-2012 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 * June 13, 2005 Allan Nathanson <ajn@apple.com>
28 * - added SCPreferences support
29 *
30 * August 4, 2004 Allan Nathanson <ajn@apple.com>
31 * - added network configuration (prefs) support
32 *
33 * September 25, 2002 Allan Nathanson <ajn@apple.com>
34 * - added command line history & editing
35 *
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
39 * dynamic store key.
40 *
41 * June 1, 2001 Allan Nathanson <ajn@apple.com>
42 * - public API conversion
43 *
44 * November 9, 2000 Allan Nathanson <ajn@apple.com>
45 * - initial revision
46 */
47
48 #include <TargetConditionals.h>
49 #include <ctype.h>
50 #include <getopt.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <termios.h>
55 #include <unistd.h>
56 #include <sysexits.h>
57
58 #ifdef DEBUG
59 #include <mach/mach.h>
60 #include <mach/mach_error.h>
61 #endif /* DEBUG */
62
63 #include "scutil.h"
64 #include "commands.h"
65 #include "dictionary.h"
66 #include "net.h"
67 #include "nc.h"
68 #include "prefs.h"
69 #include "session.h"
70 #include "tests.h"
71
72
73 #define LINE_LENGTH 256
74
75
76 __private_extern__ AuthorizationRef authorization = NULL;
77 __private_extern__ InputRef currentInput = NULL;
78 __private_extern__ Boolean doDispatch = FALSE;
79 __private_extern__ int nesting = 0;
80 __private_extern__ CFRunLoopRef notifyRl = NULL;
81 __private_extern__ CFRunLoopSourceRef notifyRls = NULL;
82 __private_extern__ SCPreferencesRef prefs = 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;
87
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 { "dns", no_argument, NULL, 0 },
98 { "get", required_argument, NULL, 0 },
99 { "help", no_argument, NULL, '?' },
100 { "nc", required_argument, NULL, 0 },
101 { "net", no_argument, NULL, 0 },
102 { "nwi", no_argument, NULL, 0 },
103 { "prefs", no_argument, NULL, 0 },
104 { "proxy", no_argument, NULL, 0 },
105 { "set", required_argument, NULL, 0 },
106 { "snapshot", no_argument, NULL, 0 },
107 { "user", required_argument, NULL, 0 },
108 { "password", required_argument, NULL, 0 },
109 { "secret", required_argument, NULL, 0 },
110 { NULL, 0, NULL, 0 }
111 };
112
113
114 __private_extern__
115 CFStringRef
116 _copyStringFromSTDIN()
117 {
118 char buf[1024];
119 size_t len;
120 CFStringRef utf8;
121
122 if (fgets(buf, sizeof(buf), stdin) == NULL) {
123 return NULL;
124 }
125
126 len = strlen(buf);
127 if (buf[len-1] == '\n') {
128 buf[--len] = '\0';
129 }
130
131 utf8 = CFStringCreateWithBytes(NULL, (UInt8 *)buf, len, kCFStringEncodingUTF8, TRUE);
132 return utf8;
133 }
134
135 static char *
136 getLine(char *buf, int len, InputRef src)
137 {
138 int n;
139
140 if (src->el) {
141 int count;
142 const char *line;
143
144 line = el_gets(src->el, &count);
145 if (line == NULL)
146 return NULL;
147
148 strncpy(buf, line, len);
149 } else {
150 if (fgets(buf, len, src->fp) == NULL)
151 return NULL;
152 }
153
154 n = strlen(buf);
155 if (buf[n-1] == '\n') {
156 /* the entire line fit in the buffer, remove the newline */
157 buf[n-1] = '\0';
158 } else if (!src->el) {
159 /* eat the remainder of the line */
160 do {
161 n = fgetc(src->fp);
162 } while ((n != '\n') && (n != EOF));
163 }
164
165 if (src->h && (buf[0] != '\0')) {
166 HistEvent ev;
167
168 history(src->h, &ev, H_ENTER, buf);
169 }
170
171
172 return buf;
173 }
174
175
176 static char *
177 getString(char **line)
178 {
179 char *s, *e, c, *string;
180 int i, isQuoted = 0, escaped = 0;
181
182 if (*line == NULL) return NULL;
183 if (**line == '\0') return NULL;
184
185 /* Skip leading white space */
186 while (isspace(**line)) *line += 1;
187
188 /* Grab the next string */
189 s = *line;
190 if (*s == '\0') {
191 return NULL; /* no string available */
192 } else if (*s == '"') {
193 isQuoted = 1; /* it's a quoted string */
194 s++;
195 }
196
197 for (e = s; (c = *e) != '\0'; e++) {
198 if (isQuoted && (c == '"'))
199 break; /* end of quoted string */
200 if (c == '\\') {
201 e++;
202 if (*e == '\0')
203 break; /* if premature end-of-string */
204 if ((*e == '"') || isspace(*e))
205 escaped++; /* if escaped quote or white space */
206 }
207 if (!isQuoted && isspace(c))
208 break; /* end of non-quoted string */
209 }
210
211 string = malloc(e - s - escaped + 1);
212
213 for (i = 0; s < e; s++) {
214 string[i] = *s;
215 if (!((s[0] == '\\') && ((s[1] == '"') || isspace(s[1])))) i++;
216 }
217 string[i] = '\0';
218
219 if (isQuoted)
220 e++; /* move past end of quoted string */
221
222 *line = e;
223 return string;
224 }
225
226
227 __private_extern__
228 Boolean
229 process_line(InputRef src)
230 {
231 char *arg;
232 int argc = 0;
233 char **argv = NULL;
234 int i;
235 char line[LINE_LENGTH];
236 char *s = line;
237
238 // if end-of-file, exit
239 if (getLine(line, sizeof(line), src) == NULL)
240 return FALSE;
241
242 if (nesting > 0) {
243 SCPrint(TRUE, stdout, CFSTR("%d> %s\n"), nesting, line);
244 }
245
246 // break up the input line
247 while ((arg = getString(&s)) != NULL) {
248 if (argc == 0)
249 argv = (char **)malloc(2 * sizeof(char *));
250 else
251 argv = (char **)reallocf(argv, ((argc + 2) * sizeof(char *)));
252 argv[argc++] = arg;
253 }
254
255 if (argc == 0) {
256 return TRUE; // if no arguments
257 }
258
259 /* process the command */
260 if (*argv[0] != '#') {
261 argv[argc] = NULL; // just in case...
262 currentInput = src;
263 do_command(argc, argv);
264 }
265
266 /* free the arguments */
267 for (i = 0; i < argc; i++) {
268 free(argv[i]);
269 }
270 free(argv);
271
272 return !termRequested;
273 }
274
275
276 static void
277 usage(const char *command)
278 {
279 SCPrint(TRUE, stderr, CFSTR("usage: %s\n"), command);
280 SCPrint(TRUE, stderr, CFSTR("\tinteractive access to the dynamic store.\n"));
281 SCPrint(TRUE, stderr, CFSTR("\n"));
282 SCPrint(TRUE, stderr, CFSTR(" or: %s --prefs [preference-file]\n"), command);
283 SCPrint(TRUE, stderr, CFSTR("\tinteractive access to the [raw] stored preferences.\n"));
284 SCPrint(TRUE, stderr, CFSTR("\n"));
285 SCPrint(TRUE, stderr, CFSTR(" or: %s [-W] -r nodename\n"), command);
286 SCPrint(TRUE, stderr, CFSTR(" or: %s [-W] -r address\n"), command);
287 SCPrint(TRUE, stderr, CFSTR(" or: %s [-W] -r local-address remote-address\n"), command);
288 SCPrint(TRUE, stderr, CFSTR("\tcheck reachability of node, address, or address pair (-W to \"watch\").\n"));
289 SCPrint(TRUE, stderr, CFSTR("\n"));
290 SCPrint(TRUE, stderr, CFSTR(" or: %s -w dynamic-store-key [ -t timeout ]\n"), command);
291 SCPrint(TRUE, stderr, CFSTR("\t-w\twait for presense of dynamic store key\n"));
292 SCPrint(TRUE, stderr, CFSTR("\t-t\ttime to wait for key\n"));
293 SCPrint(TRUE, stderr, CFSTR("\n"));
294 SCPrint(TRUE, stderr, CFSTR(" or: %s --get pref\n"), command);
295 SCPrint(TRUE, stderr, CFSTR(" or: %s --set pref [newval]\n"), command);
296 SCPrint(TRUE, stderr, CFSTR("\tpref\tdisplay (or set) the specified preference. Valid preferences\n"));
297 SCPrint(TRUE, stderr, CFSTR("\t\tinclude:\n"));
298 SCPrint(TRUE, stderr, CFSTR("\t\t\tComputerName, LocalHostName, HostName\n"));
299 SCPrint(TRUE, stderr, CFSTR("\tnewval\tNew preference value to be set. If not specified,\n"));
300 SCPrint(TRUE, stderr, CFSTR("\t\tthe new value will be read from standard input.\n"));
301 SCPrint(TRUE, stderr, CFSTR("\n"));
302 SCPrint(TRUE, stderr, CFSTR(" or: %s --dns\n"), command);
303 SCPrint(TRUE, stderr, CFSTR("\tshow DNS configuration.\n"));
304 SCPrint(TRUE, stderr, CFSTR("\n"));
305 SCPrint(TRUE, stderr, CFSTR(" or: %s --proxy\n"), command);
306 SCPrint(TRUE, stderr, CFSTR("\tshow \"proxy\" configuration.\n"));
307 SCPrint(TRUE, stderr, CFSTR("\n"));
308 SCPrint(TRUE, stderr, CFSTR(" or: %s --nwi\n"), command);
309 SCPrint(TRUE, stderr, CFSTR("\tshow network information\n"));
310
311 if (getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
312 SCPrint(TRUE, stderr, CFSTR("\n"));
313 SCPrint(TRUE, stderr, CFSTR(" or: %s --net\n"), command);
314 SCPrint(TRUE, stderr, CFSTR("\tmanage network configuration.\n"));
315 }
316
317 exit (EX_USAGE);
318 }
319
320
321 static char *
322 prompt(EditLine *el)
323 {
324 return "> ";
325 }
326
327
328 int
329 main(int argc, char * const argv[])
330 {
331 Boolean doDNS = FALSE;
332 Boolean doNet = FALSE;
333 Boolean doNWI = FALSE;
334 Boolean doPrefs = FALSE;
335 Boolean doProxy = FALSE;
336 Boolean doReach = FALSE;
337 Boolean doSnap = FALSE;
338 char *get = NULL;
339 extern int optind;
340 int opt;
341 int opti;
342 const char *prog = argv[0];
343 char *set = NULL;
344 char *nc_cmd = NULL;
345 InputRef src;
346 int timeout = 15; /* default timeout (in seconds) */
347 char *wait = NULL;
348 Boolean watch = FALSE;
349 int xStore = 0; /* non dynamic store command line options */
350
351 /* process any arguments */
352
353 while ((opt = getopt_long(argc, argv, "dDvprt:w:W", longopts, &opti)) != -1)
354 switch(opt) {
355 case 'd':
356 _sc_debug = TRUE;
357 _sc_log = FALSE; /* enable framework logging */
358 break;
359 case 'D':
360 doDispatch = TRUE;
361 break;
362 case 'v':
363 _sc_verbose = TRUE;
364 _sc_log = FALSE; /* enable framework logging */
365 break;
366 case 'p':
367 enablePrivateAPI = TRUE;
368 break;
369 case 'r':
370 doReach = TRUE;
371 xStore++;
372 break;
373 case 't':
374 timeout = atoi(optarg);
375 break;
376 case 'w':
377 wait = optarg;
378 xStore++;
379 break;
380 case 'W':
381 watch = TRUE;
382 break;
383 case 0:
384 if (strcmp(longopts[opti].name, "dns") == 0) {
385 doDNS = TRUE;
386 xStore++;
387 } else if (strcmp(longopts[opti].name, "get") == 0) {
388 get = optarg;
389 xStore++;
390 } else if (strcmp(longopts[opti].name, "nc") == 0) {
391 nc_cmd = optarg;
392 xStore++;
393 } else if (strcmp(longopts[opti].name, "net") == 0) {
394 doNet = TRUE;
395 xStore++;
396 } else if (strcmp(longopts[opti].name, "nwi") == 0) {
397 doNWI = TRUE;
398 xStore++;
399 } else if (strcmp(longopts[opti].name, "prefs") == 0) {
400 doPrefs = TRUE;
401 xStore++;
402 } else if (strcmp(longopts[opti].name, "proxy") == 0) {
403 doProxy = TRUE;
404 xStore++;
405 } else if (strcmp(longopts[opti].name, "set") == 0) {
406 set = optarg;
407 xStore++;
408 } else if (strcmp(longopts[opti].name, "snapshot") == 0) {
409 doSnap = TRUE;
410 xStore++;
411 } else if (strcmp(longopts[opti].name, "user") == 0) {
412 username = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
413 } else if (strcmp(longopts[opti].name, "password") == 0) {
414 password = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
415 } else if (strcmp(longopts[opti].name, "secret") == 0) {
416 sharedsecret = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
417 }
418 break;
419 case '?':
420 default :
421 usage(prog);
422 }
423 argc -= optind;
424 argv += optind;
425
426 if (xStore > 1) {
427 // if we are attempting to process more than one type of request
428 usage(prog);
429 }
430 /* are we checking (or watching) the reachability of a host/address */
431 if (doReach) {
432 if (argc < 1) {
433 usage(prog);
434 }
435 if (watch) {
436 do_watchReachability(argc, (char **)argv);
437 } else {
438 do_checkReachability(argc, (char **)argv);
439 }
440 /* NOT REACHED */
441 }
442
443 /* are we waiting on the presense of a dynamic store key */
444 if (wait) {
445 do_wait(wait, timeout);
446 /* NOT REACHED */
447 }
448
449 /* are we looking up the DNS configuration */
450 if (doDNS) {
451 do_showDNSConfiguration(argc, (char **)argv);
452 /* NOT REACHED */
453 }
454
455 if (doNWI) {
456 do_nwi(argc, (char**)argv);
457 /* NOT REACHED */
458 }
459
460 if (doSnap) {
461 if (!enablePrivateAPI || (geteuid() != 0)) {
462 usage(prog);
463 }
464
465 do_open(0, NULL); /* open the dynamic store */
466 do_snapshot(argc, (char**)argv);
467 exit(0);
468 }
469
470 /* are we looking up a preference value */
471 if (get) {
472 if (findPref(get) < 0) {
473 usage(prog);
474 }
475 do_getPref(get, argc, (char **)argv);
476 /* NOT REACHED */
477 }
478
479 /* are we looking up the proxy configuration */
480 if (doProxy) {
481 do_showProxyConfiguration(argc, (char **)argv);
482 /* NOT REACHED */
483 }
484
485 /* are we changing a preference value */
486 if (set) {
487 if (findPref(set) < 0) {
488 usage(prog);
489 }
490 do_setPref(set, argc, (char **)argv);
491 /* NOT REACHED */
492 }
493
494 /* network connection commands */
495 if (nc_cmd) {
496 if (find_nc_cmd(nc_cmd) < 0) {
497 usage(prog);
498 }
499 do_nc_cmd(nc_cmd, argc, (char **)argv, watch);
500 /* NOT REACHED */
501 }
502
503 if (doNet) {
504 /* if we are going to be managing the network configuration */
505 commands = (cmdInfo *)commands_net;
506 nCommands = nCommands_net;
507
508 if (!getenv("ENABLE_EXPERIMENTAL_SCUTIL_COMMANDS")) {
509 usage(prog);
510 }
511
512 do_net_init(); /* initialization */
513 do_net_open(0, NULL); /* open default prefs */
514 } else if (doPrefs) {
515 /* if we are going to be managing the network configuration */
516 commands = (cmdInfo *)commands_prefs;
517 nCommands = nCommands_prefs;
518
519 do_dictInit(0, NULL); /* start with an empty dictionary */
520 do_prefs_init(); /* initialization */
521 do_prefs_open(argc, (char **)argv); /* open prefs */
522 } else {
523 /* if we are going to be managing the dynamic store */
524 commands = (cmdInfo *)commands_store;
525 nCommands = nCommands_store;
526
527 do_dictInit(0, NULL); /* start with an empty dictionary */
528 do_open(0, NULL); /* open the dynamic store */
529 }
530
531 /* allocate command input stream */
532 src = (InputRef)CFAllocatorAllocate(NULL, sizeof(Input), 0);
533 src->fp = stdin;
534 src->el = NULL;
535 src->h = NULL;
536
537 if (isatty(fileno(src->fp))) {
538 int editmode = 1;
539 HistEvent ev;
540 struct termios t;
541
542 if (tcgetattr(fileno(src->fp), &t) != -1) {
543 if ((t.c_lflag & ECHO) == 0) {
544 editmode = 0;
545 }
546 }
547 src->el = el_init(prog, src->fp, stdout, stderr);
548 src->h = history_init();
549
550 (void)history(src->h, &ev, H_SETSIZE, INT_MAX);
551 el_set(src->el, EL_HIST, history, src->h);
552
553 if (!editmode) {
554 el_set(src->el, EL_EDITMODE, 0);
555 }
556
557 el_set(src->el, EL_EDITOR, "emacs");
558 el_set(src->el, EL_PROMPT, prompt);
559
560 el_source(src->el, NULL);
561
562 if ((el_get(src->el, EL_EDITMODE, &editmode) != -1) && editmode != 0) {
563 el_set(src->el, EL_SIGNAL, 1);
564 } else {
565 history_end(src->h);
566 src->h = NULL;
567 el_end(src->el);
568 src->el = NULL;
569 }
570 }
571
572 while (TRUE) {
573 Boolean ok;
574
575 ok = process_line(src);
576 if (!ok) {
577 break;
578 }
579 }
580
581 /* close the socket, free resources */
582 if (src->h) history_end(src->h);
583 if (src->el) el_end(src->el);
584 (void)fclose(src->fp);
585 CFAllocatorDeallocate(NULL, src);
586
587 exit (EX_OK); // insure the process exit status is 0
588 return 0; // ...and make main fit the ANSI spec.
589 }