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