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