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