]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/scutil.c
ecea776f359ffa6eb554fe3a9510ffc0de6bb07e
[apple/configd.git] / scutil.tproj / scutil.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26 /*
27 * Modification History
28 *
29 * September 25, 2002 Allan Nathanson <ajn@apple.com>
30 * - added command line history & editing
31 *
32 * July 9, 2001 Allan Nathanson <ajn@apple.com>
33 * - added "-r" option for checking network reachability
34 * - added "-w" option to check/wait for the presence of a
35 * dynamic store key.
36 *
37 * June 1, 2001 Allan Nathanson <ajn@apple.com>
38 * - public API conversion
39 *
40 * November 9, 2000 Allan Nathanson <ajn@apple.com>
41 * - initial revision
42 */
43
44 #include <ctype.h>
45 #include <getopt.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <termios.h>
50 #include <unistd.h>
51 #include <sysexits.h>
52
53 #ifdef DEBUG
54 #include <mach/mach.h>
55 #include <mach/mach_error.h>
56 #endif /* DEBUG */
57
58 #include "scutil.h"
59 #include "commands.h"
60 #include "dictionary.h"
61 #include "tests.h"
62 #include "prefs.h"
63
64 #include "SCDynamicStoreInternal.h"
65
66
67 #define LINE_LENGTH 256
68
69
70 int nesting = 0;
71 CFRunLoopRef notifyRl = NULL;
72 CFRunLoopSourceRef notifyRls = NULL;
73 SCDynamicStoreRef store = NULL;
74 CFPropertyListRef value = NULL;
75 CFMutableArrayRef watchedKeys = NULL;
76 CFMutableArrayRef watchedPatterns = NULL;
77
78 static struct option longopts[] = {
79 // { "debug", no_argument, NULL, 'd' },
80 // { "verbose", no_argument, NULL, 'v' },
81 // { "SPI", no_argument, NULL, 'p' },
82 // { "check-reachability", required_argument, NULL, 'r' },
83 // { "timeout", required_argument, NULL, 't' },
84 // { "wait-key", required_argument, NULL, 'w' },
85 { "get", required_argument, NULL, 0 },
86 { "help", no_argument, NULL, '?' },
87 { "set", required_argument, NULL, 0 },
88 { NULL, 0, NULL, 0 }
89 };
90
91
92 static char *
93 getLine(char *buf, int len, InputRef src)
94 {
95 int n;
96
97 if (src->el) {
98 int count;
99 const char *line;
100
101 line = el_gets(src->el, &count);
102 if (line == NULL)
103 return NULL;
104
105 strncpy(buf, line, len);
106 } else {
107 if (fgets(buf, len, src->fp) == NULL)
108 return NULL;
109 }
110
111 n = strlen(buf);
112 if (buf[n-1] == '\n') {
113 /* the entire line fit in the buffer, remove the newline */
114 buf[n-1] = '\0';
115 } else if (!src->el) {
116 /* eat the remainder of the line */
117 do {
118 n = fgetc(src->fp);
119 } while ((n != '\n') && (n != EOF));
120 }
121
122 if (src->h) {
123 HistEvent ev;
124
125 history(src->h, &ev, H_ENTER, buf);
126 }
127
128 return buf;
129 }
130
131
132 char *
133 getString(char **line)
134 {
135 char *s, *e, c, *string;
136 int i, isQuoted = 0, escaped = 0;
137
138 if (*line == NULL) return NULL;
139 if (**line == '\0') return NULL;
140
141 /* Skip leading white space */
142 while (isspace(**line)) *line += 1;
143
144 /* Grab the next string */
145 s = *line;
146 if (*s == '\0') {
147 return NULL; /* no string available */
148 } else if (*s == '"') {
149 isQuoted = 1; /* it's a quoted string */
150 s++;
151 }
152
153 for (e = s; (c = *e) != '\0'; e++) {
154 if (isQuoted && (c == '"'))
155 break; /* end of quoted string */
156 if (c == '\\') {
157 e++;
158 if (*e == '\0')
159 break; /* if premature end-of-string */
160 if ((*e == '"') || isspace(*e))
161 escaped++; /* if escaped quote or white space */
162 }
163 if (!isQuoted && isspace(c))
164 break; /* end of non-quoted string */
165 }
166
167 string = malloc(e - s - escaped + 1);
168
169 for (i = 0; s < e; s++) {
170 string[i] = *s;
171 if (!((s[0] == '\\') && ((s[1] == '"') || isspace(s[1])))) i++;
172 }
173 string[i] = '\0';
174
175 if (isQuoted)
176 e++; /* move past end of quoted string */
177
178 *line = e;
179 return string;
180 }
181
182
183 Boolean
184 process_line(InputRef src)
185 {
186 char line[LINE_LENGTH], *s, *arg, **argv = NULL;
187 int i, argc;
188
189 /* if end-of-file, exit */
190 if (getLine(line, sizeof(line), src) == NULL)
191 return FALSE;
192
193 if (nesting > 0) {
194 SCPrint(TRUE, stdout, CFSTR("%d> %s\n"), nesting, line);
195 }
196
197 /* if requested, exit */
198 if (strcasecmp(line, "exit") == 0) return FALSE;
199 if (strcasecmp(line, "quit") == 0) return FALSE;
200 if (strcasecmp(line, "q" ) == 0) return FALSE;
201
202 /* break up the input line */
203 s = line;
204 argc = 0;
205 while ((arg = getString(&s)) != NULL) {
206 if (argc == 0)
207 argv = (char **)malloc(2 * sizeof(char *));
208 else
209 argv = (char **)realloc(argv, ((argc + 2) * sizeof(char *)));
210 argv[argc++] = arg;
211 }
212
213 /* process the command */
214 if (argc > 0) {
215 argv[argc] = NULL; /* just in case... */
216
217 if (*argv[0] != '#')
218 do_command(argc, argv);
219
220 for (i = 0; i < argc; i++)
221 free(argv[i]);
222 free(argv);
223 }
224
225 return TRUE;
226 }
227
228
229 void
230 usage(const char *command)
231 {
232 SCPrint(TRUE, stderr, CFSTR("usage: %s\n"), command);
233 SCPrint(TRUE, stderr, CFSTR("\tinteractive access to the dynamic store.\n"));
234 SCPrint(TRUE, stderr, CFSTR("\n"));
235 SCPrint(TRUE, stderr, CFSTR(" or: %s -r nodename\n"), command);
236 SCPrint(TRUE, stderr, CFSTR(" or: %s -r address\n"), command);
237 SCPrint(TRUE, stderr, CFSTR(" or: %s -r local-address remote-address\n"), command);
238 SCPrint(TRUE, stderr, CFSTR("\tcheck reachability of node, address, or address pair.\n"));
239 SCPrint(TRUE, stderr, CFSTR("\n"));
240 SCPrint(TRUE, stderr, CFSTR(" or: %s -w dynamic-store-key [ -t timeout ]\n"), command);
241 SCPrint(TRUE, stderr, CFSTR("\t-w\twait for presense of dynamic store key\n"));
242 SCPrint(TRUE, stderr, CFSTR("\t-t\ttime to wait for key\n"));
243 SCPrint(TRUE, stderr, CFSTR("\n"));
244 SCPrint(TRUE, stderr, CFSTR(" or: %s --get pref\n"), command);
245 SCPrint(TRUE, stderr, CFSTR(" or: %s --set pref [newval]\n"), command);
246 SCPrint(TRUE, stderr, CFSTR("\tpref\tdisplay (or set) the specified preference. Valid preferences\n"));
247 SCPrint(TRUE, stderr, CFSTR("\t\tinclude:\n"));
248 SCPrint(TRUE, stderr, CFSTR("\t\t\tComputerName, LocalHostName\n"));
249 SCPrint(TRUE, stderr, CFSTR("\tnewval\tNew preference value to be set. If not specified,\n"));
250 SCPrint(TRUE, stderr, CFSTR("\t\tthe new value will be read from standard input.\n"));
251 exit (EX_USAGE);
252 }
253
254
255 static char *
256 prompt(EditLine *el)
257 {
258 return "> ";
259 }
260
261
262 int
263 main(int argc, char * const argv[])
264 {
265 char *get = NULL;
266 extern int optind;
267 int opt;
268 int opti;
269 const char *prog = argv[0];
270 Boolean reach = FALSE;
271 char *set = NULL;
272 InputRef src;
273 int timeout = 15; /* default timeout (in seconds) */
274 char *wait = NULL;
275 int xStore = 0; /* non dynamic store command line options */
276
277 /* process any arguments */
278
279 while ((opt = getopt_long(argc, argv, "dvprt:w:", longopts, &opti)) != -1)
280 switch(opt) {
281 case 'd':
282 _sc_debug = TRUE;
283 _sc_log = FALSE; /* enable framework logging */
284 break;
285 case 'v':
286 _sc_verbose = TRUE;
287 _sc_log = FALSE; /* enable framework logging */
288 break;
289 case 'p':
290 enablePrivateAPI = TRUE;
291 break;
292 case 'r':
293 reach = TRUE;
294 xStore++;
295 break;
296 case 't':
297 timeout = atoi(optarg);
298 break;
299 case 'w':
300 wait = optarg;
301 xStore++;
302 break;
303 case 0:
304 if (strcmp(longopts[opti].name, "get") == 0) {
305 get = optarg;
306 xStore++;
307 } else if (strcmp(longopts[opti].name, "set") == 0) {
308 set = optarg;
309 xStore++;
310 }
311 break;
312 case '?':
313 default :
314 usage(prog);
315 }
316 argc -= optind;
317 argv += optind;
318
319 if (xStore > 1) {
320 // if we are attempting to process more than one type of request
321 usage(prog);
322 }
323
324 /* are we checking the reachability of a host/address */
325 if (reach) {
326 if ((argc < 1) || (argc > 2)) {
327 usage(prog);
328 }
329 do_checkReachability(argc, (char **)argv);
330 /* NOT REACHED */
331 }
332
333 /* are we waiting on the presense of a dynamic store key */
334 if (wait) {
335 do_wait(wait, timeout);
336 /* NOT REACHED */
337 }
338
339 /* are we looking up a preference value */
340 if (get) {
341 if (findPref(get) < 0) {
342 usage(prog);
343 }
344 do_getPref(get, argc, (char **)argv);
345 /* NOT REACHED */
346 }
347
348 /* are we changing a preference value */
349 if (set) {
350 if (findPref(set) < 0) {
351 usage(prog);
352 }
353 do_setPref(set, argc, (char **)argv);
354 /* NOT REACHED */
355 }
356
357 /* start with an empty dictionary */
358 do_dictInit(0, NULL);
359
360 /* allocate command input stream */
361 src = (InputRef)CFAllocatorAllocate(NULL, sizeof(Input), 0);
362 src->fp = stdin;
363 src->el = NULL;
364 src->h = NULL;
365
366 if (isatty(fileno(src->fp))) {
367 int editmode = 1;
368 HistEvent ev;
369 struct termios t;
370
371 if (tcgetattr(fileno(src->fp), &t) != -1) {
372 if ((t.c_lflag & ECHO) == 0) {
373 editmode = 0;
374 }
375 }
376 src->el = el_init(prog, src->fp, stdout, stderr);
377 src->h = history_init();
378
379 (void)history(src->h, &ev, H_SETSIZE, INT_MAX);
380 el_set(src->el, EL_HIST, history, src->h);
381
382 if (!editmode) {
383 el_set(src->el, EL_EDITMODE, 0);
384 }
385
386 el_set(src->el, EL_EDITOR, "emacs");
387 el_set(src->el, EL_PROMPT, prompt);
388
389 el_source(src->el, NULL);
390
391 if ((el_get(src->el, EL_EDITMODE, &editmode) != -1) && editmode != 0) {
392 el_set(src->el, EL_SIGNAL, 1);
393 } else {
394 history_end(src->h);
395 src->h = NULL;
396 el_end(src->el);
397 src->el = NULL;
398 }
399 }
400
401 while (process_line(src) == TRUE) {
402 /* debug information, diagnostics */
403 __showMachPortStatus();
404 }
405
406 /* close the socket, free resources */
407 if (src->h) history_end(src->h);
408 if (src->el) el_end(src->el);
409 (void)fclose(src->fp);
410 CFAllocatorDeallocate(NULL, src);
411
412 exit (EX_OK); // insure the process exit status is 0
413 return 0; // ...and make main fit the ANSI spec.
414 }