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