]> git.saurik.com Git - apple/configd.git/blame - scutil.tproj/scutil.c
configd-289.2.tar.gz
[apple/configd.git] / scutil.tproj / scutil.c
CommitLineData
5958d7c0 1/*
a40a14f8 2 * Copyright (c) 2000-2009 Apple 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
5958d7c0
A
70
71#define LINE_LENGTH 256
72
0fae82ee 73
edebe297 74__private_extern__ AuthorizationRef authorization = NULL;
dbf6a266
A
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;
009ee3c6 84
dbf6a266 85static const struct option longopts[] = {
009ee3c6
A
86// { "debug", no_argument, NULL, 'd' },
87// { "verbose", no_argument, NULL, 'v' },
88// { "SPI", no_argument, NULL, 'p' },
dbf6a266 89// { "check-reachability", required_argument, NULL, 'r' },
009ee3c6
A
90// { "timeout", required_argument, NULL, 't' },
91// { "wait-key", required_argument, NULL, 'w' },
dbf6a266 92 { "dns", no_argument, NULL, 0 },
009ee3c6
A
93 { "get", required_argument, NULL, 0 },
94 { "help", no_argument, NULL, '?' },
dbf6a266 95 { "net", no_argument, NULL, 0 },
edebe297 96 { "prefs", no_argument, NULL, 0 },
dbf6a266 97 { "proxy", no_argument, NULL, 0 },
009ee3c6 98 { "set", required_argument, NULL, 0 },
dbf6a266 99 { NULL, 0, NULL, 0 }
009ee3c6 100};
5958d7c0
A
101
102
103static char *
009ee3c6 104getLine(char *buf, int len, InputRef src)
5958d7c0 105{
009ee3c6 106 int n;
5958d7c0 107
009ee3c6
A
108 if (src->el) {
109 int count;
110 const char *line;
5958d7c0 111
009ee3c6
A
112 line = el_gets(src->el, &count);
113 if (line == NULL)
114 return NULL;
115
116 strncpy(buf, line, len);
5958d7c0 117 } else {
009ee3c6
A
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) {
5958d7c0
A
127 /* eat the remainder of the line */
128 do {
009ee3c6
A
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);
5958d7c0
A
137 }
138
ba83da55 139
5958d7c0
A
140 return buf;
141}
142
143
dbf6a266 144static char *
5958d7c0
A
145getString(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
dbf6a266 195__private_extern__
0fae82ee 196Boolean
009ee3c6 197process_line(InputRef src)
5958d7c0 198{
dbf6a266
A
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
009ee3c6 207 if (getLine(line, sizeof(line), src) == NULL)
5958d7c0
A
208 return FALSE;
209
0fae82ee 210 if (nesting > 0) {
a5f60add 211 SCPrint(TRUE, stdout, CFSTR("%d> %s\n"), nesting, line);
5958d7c0
A
212 }
213
dbf6a266 214 // break up the input line
5958d7c0
A
215 while ((arg = getString(&s)) != NULL) {
216 if (argc == 0)
217 argv = (char **)malloc(2 * sizeof(char *));
218 else
dbf6a266 219 argv = (char **)reallocf(argv, ((argc + 2) * sizeof(char *)));
5958d7c0
A
220 argv[argc++] = arg;
221 }
222
dbf6a266
A
223 if (argc == 0) {
224 return TRUE; // if no arguments
225 }
5958d7c0 226
dbf6a266
A
227 /* process the command */
228 if (*argv[0] != '#') {
229 argv[argc] = NULL; // just in case...
230 currentInput = src;
231 do_command(argc, argv);
232 }
5958d7c0 233
dbf6a266
A
234 /* free the arguments */
235 for (i = 0; i < argc; i++) {
236 free(argv[i]);
5958d7c0 237 }
dbf6a266 238 free(argv);
5958d7c0 239
dbf6a266 240 return !termRequested;
5958d7c0
A
241}
242
243
dbf6a266 244static void
0fae82ee
A
245usage(const char *command)
246{
247 SCPrint(TRUE, stderr, CFSTR("usage: %s\n"), command);
009ee3c6
A
248 SCPrint(TRUE, stderr, CFSTR("\tinteractive access to the dynamic store.\n"));
249 SCPrint(TRUE, stderr, CFSTR("\n"));
edebe297
A
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"));
a40a14f8
A
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"));
009ee3c6 257 SCPrint(TRUE, stderr, CFSTR("\n"));
0fae82ee
A
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"));
009ee3c6
A
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"));
edebe297 266 SCPrint(TRUE, stderr, CFSTR("\t\t\tComputerName, LocalHostName, HostName\n"));
009ee3c6
A
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"));
dbf6a266
A
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
0fae82ee
A
282 exit (EX_USAGE);
283}
284
285
009ee3c6
A
286static char *
287prompt(EditLine *el)
288{
289 return "> ";
290}
291
292
5958d7c0 293int
a5f60add 294main(int argc, char * const argv[])
5958d7c0 295{
edebe297
A
296 Boolean doDNS = FALSE;
297 Boolean doNet = FALSE;
298 Boolean doPrefs = FALSE;
299 Boolean doProxy = FALSE;
300 Boolean doReach = FALSE;
009ee3c6 301 char *get = NULL;
0fae82ee
A
302 extern int optind;
303 int opt;
009ee3c6 304 int opti;
0fae82ee 305 const char *prog = argv[0];
009ee3c6
A
306 char *set = NULL;
307 InputRef src;
0fae82ee
A
308 int timeout = 15; /* default timeout (in seconds) */
309 char *wait = NULL;
a40a14f8 310 Boolean watch = FALSE;
009ee3c6 311 int xStore = 0; /* non dynamic store command line options */
5958d7c0
A
312
313 /* process any arguments */
314
a40a14f8 315 while ((opt = getopt_long(argc, argv, "dvprt:w:W", longopts, &opti)) != -1)
5958d7c0
A
316 switch(opt) {
317 case 'd':
0fae82ee
A
318 _sc_debug = TRUE;
319 _sc_log = FALSE; /* enable framework logging */
5958d7c0
A
320 break;
321 case 'v':
0fae82ee
A
322 _sc_verbose = TRUE;
323 _sc_log = FALSE; /* enable framework logging */
324 break;
325 case 'p':
326 enablePrivateAPI = TRUE;
5958d7c0
A
327 break;
328 case 'r':
edebe297 329 doReach = TRUE;
009ee3c6 330 xStore++;
0fae82ee
A
331 break;
332 case 't':
333 timeout = atoi(optarg);
334 break;
335 case 'w':
336 wait = optarg;
009ee3c6
A
337 xStore++;
338 break;
a40a14f8
A
339 case 'W':
340 watch = TRUE;
341 break;
009ee3c6 342 case 0:
dbf6a266 343 if (strcmp(longopts[opti].name, "dns") == 0) {
edebe297 344 doDNS = TRUE;
dbf6a266
A
345 xStore++;
346 } else if (strcmp(longopts[opti].name, "get") == 0) {
009ee3c6
A
347 get = optarg;
348 xStore++;
dbf6a266 349 } else if (strcmp(longopts[opti].name, "net") == 0) {
edebe297
A
350 doNet = TRUE;
351 xStore++;
352 } else if (strcmp(longopts[opti].name, "prefs") == 0) {
353 doPrefs = TRUE;
dbf6a266
A
354 xStore++;
355 } else if (strcmp(longopts[opti].name, "proxy") == 0) {
edebe297 356 doProxy = TRUE;
dbf6a266 357 xStore++;
009ee3c6
A
358 } else if (strcmp(longopts[opti].name, "set") == 0) {
359 set = optarg;
360 xStore++;
361 }
5958d7c0
A
362 break;
363 case '?':
364 default :
0fae82ee 365 usage(prog);
a5f60add 366 }
5958d7c0
A
367 argc -= optind;
368 argv += optind;
369
009ee3c6
A
370 if (xStore > 1) {
371 // if we are attempting to process more than one type of request
0fae82ee
A
372 usage(prog);
373 }
374
a40a14f8 375 /* are we checking (or watching) the reachability of a host/address */
edebe297 376 if (doReach) {
a40a14f8 377 if (argc < 1) {
009ee3c6
A
378 usage(prog);
379 }
a40a14f8
A
380 if (watch) {
381 do_watchReachability(argc, (char **)argv);
382 } else {
383 do_checkReachability(argc, (char **)argv);
384 }
0fae82ee
A
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
dbf6a266 394 /* are we looking up the DNS configuration */
edebe297 395 if (doDNS) {
dbf6a266
A
396 do_showDNSConfiguration(argc, (char **)argv);
397 /* NOT REACHED */
398 }
399
009ee3c6
A
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
dbf6a266 409 /* are we looking up the proxy configuration */
edebe297 410 if (doProxy) {
dbf6a266
A
411 do_showProxyConfiguration(argc, (char **)argv);
412 /* NOT REACHED */
413 }
414
009ee3c6
A
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
edebe297 424 if (doNet) {
dbf6a266 425 /* if we are going to be managing the network configuration */
edebe297
A
426 commands = (cmdInfo *)commands_net;
427 nCommands = nCommands_net;
dbf6a266
A
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 */
edebe297
A
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 */
dbf6a266
A
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 }
5958d7c0 451
009ee3c6
A
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);
5958d7c0 473
009ee3c6
A
474 if (!editmode) {
475 el_set(src->el, EL_EDITMODE, 0);
476 }
5958d7c0 477
009ee3c6
A
478 el_set(src->el, EL_EDITOR, "emacs");
479 el_set(src->el, EL_PROMPT, prompt);
5958d7c0 480
009ee3c6 481 el_source(src->el, NULL);
5958d7c0 482
009ee3c6
A
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 }
5958d7c0 492
a40a14f8
A
493 while (TRUE) {
494 Boolean ok;
495
496 ok = process_line(src);
497 if (!ok) {
498 break;
499 }
5958d7c0
A
500 }
501
009ee3c6
A
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);
5958d7c0
A
507
508 exit (EX_OK); // insure the process exit status is 0
509 return 0; // ...and make main fit the ANSI spec.
510}