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