]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/scutil.c
configd-53.tar.gz
[apple/configd.git] / scutil.tproj / scutil.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 /*
24 * Modification History
25 *
26 * July 9, 2001 Allan Nathanson <ajn@apple.com>
27 * - added "-r" option for checking network reachability
28 * - added "-w" option to check/wait for the presence of a
29 * dynamic store key.
30 *
31 * June 1, 2001 Allan Nathanson <ajn@apple.com>
32 * - public API conversion
33 *
34 * November 9, 2000 Allan Nathanson <ajn@apple.com>
35 * - initial revision
36 */
37
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sysexits.h>
44
45 #ifdef DEBUG
46 #include <mach/mach.h>
47 #include <mach/mach_error.h>
48 #endif /* DEBUG */
49
50 #include "scutil.h"
51 #include "commands.h"
52 #include "dictionary.h"
53 #include "tests.h"
54
55 #include <SystemConfiguration/SCPrivate.h>
56 #include "SCDynamicStoreInternal.h"
57
58
59 #define LINE_LENGTH 256
60
61
62 int nesting = 0;
63 CFRunLoopSourceRef notifyRls = NULL;
64 CFMutableArrayRef sources = NULL;
65 SCDynamicStoreRef store = NULL;
66 CFPropertyListRef value = NULL;
67
68
69 static char *
70 getLine(char *buf, int len, FILE *fp)
71 {
72 int x;
73
74 if (fgets(buf, len, fp) == NULL)
75 return NULL;
76
77 x = strlen(buf);
78 if (buf[x-1] == '\n') {
79 /* the entire line fit in the buffer, remove the newline */
80 buf[x-1] = '\0';
81 } else {
82 /* eat the remainder of the line */
83 do {
84 x = fgetc(fp);
85 } while ((x != '\n') && (x != EOF));
86 }
87
88 return buf;
89 }
90
91
92 char *
93 getString(char **line)
94 {
95 char *s, *e, c, *string;
96 int i, isQuoted = 0, escaped = 0;
97
98 if (*line == NULL) return NULL;
99 if (**line == '\0') return NULL;
100
101 /* Skip leading white space */
102 while (isspace(**line)) *line += 1;
103
104 /* Grab the next string */
105 s = *line;
106 if (*s == '\0') {
107 return NULL; /* no string available */
108 } else if (*s == '"') {
109 isQuoted = 1; /* it's a quoted string */
110 s++;
111 }
112
113 for (e = s; (c = *e) != '\0'; e++) {
114 if (isQuoted && (c == '"'))
115 break; /* end of quoted string */
116 if (c == '\\') {
117 e++;
118 if (*e == '\0')
119 break; /* if premature end-of-string */
120 if ((*e == '"') || isspace(*e))
121 escaped++; /* if escaped quote or white space */
122 }
123 if (!isQuoted && isspace(c))
124 break; /* end of non-quoted string */
125 }
126
127 string = malloc(e - s - escaped + 1);
128
129 for (i = 0; s < e; s++) {
130 string[i] = *s;
131 if (!((s[0] == '\\') && ((s[1] == '"') || isspace(s[1])))) i++;
132 }
133 string[i] = '\0';
134
135 if (isQuoted)
136 e++; /* move past end of quoted string */
137
138 *line = e;
139 return string;
140 }
141
142
143 Boolean
144 process_line(FILE *fp)
145 {
146 char line[LINE_LENGTH], *s, *arg, **argv = NULL;
147 int i, argc;
148
149 /* if end-of-file, exit */
150 if (getLine(line, sizeof(line), fp) == NULL)
151 return FALSE;
152
153 if (nesting > 0) {
154 SCPrint(TRUE, stdout, CFSTR("%d> %s\n"), nesting, line);
155 }
156
157 /* if requested, exit */
158 if (strcasecmp(line, "exit") == 0) return FALSE;
159 if (strcasecmp(line, "quit") == 0) return FALSE;
160 if (strcasecmp(line, "q" ) == 0) return FALSE;
161
162 /* break up the input line */
163 s = line;
164 argc = 0;
165 while ((arg = getString(&s)) != NULL) {
166 if (argc == 0)
167 argv = (char **)malloc(2 * sizeof(char *));
168 else
169 argv = (char **)realloc(argv, ((argc + 2) * sizeof(char *)));
170 argv[argc++] = arg;
171 }
172
173 /* process the command */
174 if (argc > 0) {
175 argv[argc] = NULL; /* just in case... */
176
177 if (*argv[0] != '#')
178 do_command(argc, argv);
179
180 for (i = 0; i < argc; i++)
181 free(argv[i]);
182 free(argv);
183 }
184
185 return TRUE;
186 }
187
188
189 void
190 runLoopProcessInput(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
191 {
192 FILE *fp = info;
193
194 if (process_line(fp) == FALSE) {
195 /* we don't want any more input from this stream, stop listening */
196 CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
197 (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
198 kCFRunLoopDefaultMode);
199
200 /* we no longer need the fd (socket) */
201 CFSocketInvalidate(s);
202
203 /* we no longer need to track this source */
204 CFArrayRemoveValueAtIndex(sources, 0);
205
206 if (CFArrayGetCount(sources) > 0) {
207 /* add the previous input source to the run loop */
208 CFRunLoopAddSource(CFRunLoopGetCurrent(),
209 (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
210 kCFRunLoopDefaultMode);
211 } else {
212 /* no more input sources, we're done! */
213 exit (EX_OK);
214 }
215
216 /* decrement the nesting level */
217 nesting--;
218 }
219
220 /* debug information, diagnostics */
221 __showMachPortStatus();
222
223 /* if necessary, re-issue prompt */
224 if ((CFArrayGetCount(sources) == 1) && isatty(STDIN_FILENO)) {
225 printf("> ");
226 fflush(stdout);
227 }
228
229 return;
230 }
231
232
233 void
234 usage(const char *command)
235 {
236 SCPrint(TRUE, stderr, CFSTR("usage: %s\n"), command);
237 SCPrint(TRUE, stderr, CFSTR(" or: %s -r node-or-address\n"), command);
238 SCPrint(TRUE, stderr, CFSTR("\t-r\tcheck reachability of node/address\n"));
239 SCPrint(TRUE, stderr, CFSTR(" or: %s -w dynamic-store-key [ -t timeout ]\n"), command);
240 SCPrint(TRUE, stderr, CFSTR("\t-w\twait for presense of dynamic store key\n"));
241 SCPrint(TRUE, stderr, CFSTR("\t-t\ttime to wait for key\n"));
242 SCPrint(TRUE, stderr, CFSTR("\n"));
243 SCPrint(TRUE, stderr, CFSTR("Note: you may only specify one of \"-r\" or \"-w\".\n"));
244 exit (EX_USAGE);
245 }
246
247
248 int
249 main(int argc, char * const argv[])
250 {
251 CFSocketContext context = { 0, stdin, NULL, NULL, NULL };
252 char *dest = NULL;
253 CFSocketRef in;
254 extern int optind;
255 int opt;
256 const char *prog = argv[0];
257 CFRunLoopSourceRef rls;
258 int timeout = 15; /* default timeout (in seconds) */
259 char *wait = NULL;
260
261 /* process any arguments */
262
263 while ((opt = getopt(argc, argv, "dvpr:t:w:")) != -1)
264 switch(opt) {
265 case 'd':
266 _sc_debug = TRUE;
267 _sc_log = FALSE; /* enable framework logging */
268 break;
269 case 'v':
270 _sc_verbose = TRUE;
271 _sc_log = FALSE; /* enable framework logging */
272 break;
273 case 'p':
274 enablePrivateAPI = TRUE;
275 break;
276 case 'r':
277 dest = optarg;
278 break;
279 case 't':
280 timeout = atoi(optarg);
281 break;
282 case 'w':
283 wait = optarg;
284 break;
285 case '?':
286 default :
287 usage(prog);
288 }
289 argc -= optind;
290 argv += optind;
291
292 if (dest && wait) {
293 usage(prog);
294 }
295
296 /* are we checking the reachability of a host/address */
297 if (dest) {
298 do_checkReachability(dest);
299 /* NOT REACHED */
300 }
301
302 /* are we waiting on the presense of a dynamic store key */
303 if (wait) {
304 do_wait(wait, timeout);
305 /* NOT REACHED */
306 }
307
308 /* start with an empty dictionary */
309 do_dictInit(0, NULL);
310
311 /* create a "socket" reference with the file descriptor associated with stdin */
312 in = CFSocketCreateWithNative(NULL,
313 STDIN_FILENO,
314 kCFSocketReadCallBack,
315 runLoopProcessInput,
316 &context);
317
318 /* Create a run loop source for the (stdin) file descriptor */
319 rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
320
321 /* keep track of input file sources */
322 sources = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
323 CFArrayAppendValue(sources, rls);
324
325 /* add this source to the run loop */
326 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
327 CFRelease(rls);
328 CFRelease(in);
329
330 /* show (initial) debug information, diagnostics */
331 __showMachPortStatus();
332
333 /* issue (initial) prompt */
334 if (isatty(STDIN_FILENO)) {
335 printf("> ");
336 fflush(stdout);
337 }
338
339 CFRunLoopRun(); /* process input, process events */
340
341 exit (EX_OK); // insure the process exit status is 0
342 return 0; // ...and make main fit the ANSI spec.
343 }