]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/configd.m
925dc7bc0401d6642e28bdb299360647530623ec
[apple/configd.git] / configd.tproj / configd.m
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 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
31 *
32 * 24 March 2000 Allan Nathanson (ajn@apple.com)
33 * - created
34 */
35
36 #include <getopt.h>
37 #include <stdio.h>
38 #include <sysexits.h>
39 #include <syslog.h>
40 #include <unistd.h>
41 #include <paths.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <objc/objc-runtime.h>
46
47 #include "configd.h"
48 #include "configd_server.h"
49 #include "plugin_support.h"
50
51 Boolean _configd_verbose = FALSE; /* TRUE if verbose logging enabled */
52
53 __private_extern__
54 FILE *_configd_trace = NULL; /* non-NULL if tracing enabled */
55
56 __private_extern__
57 CFMutableSetRef _plugins_exclude = NULL; /* bundle identifiers to exclude from loading */
58
59 __private_extern__
60 CFMutableSetRef _plugins_verbose = NULL; /* bundle identifiers to enable verbose logging */
61
62 static CFMachPortRef termRequested = NULL; /* Mach port used to notify runloop of a shutdown request */
63
64
65 static struct option longopts[] = {
66 // { "no-bundles", no_argument, 0, 'b' },
67 // { "exclude-plugin", required_argument, 0, 'B' },
68 // { "no-fork", no_argument, 0, 'd' },
69 // { "test-bundle", required_argument, 0, 't' },
70 // { "verbose", no_argument, 0, 'v' },
71 // { "verbose-bundle", required_argument, 0, 'V' },
72 { "help", no_argument, 0, '?' },
73 { 0, 0, 0, 0 }
74 };
75
76
77 static void
78 usage(const char *prog)
79 {
80 SCPrint(TRUE, stderr, CFSTR("%s: [-d] [-v] [-V bundleID] [-b] [-B bundleID] [-t plugin-path]\n"), prog);
81 SCPrint(TRUE, stderr, CFSTR("options:\n"));
82 SCPrint(TRUE, stderr, CFSTR("\t-d\tdisable daemon/run in foreground\n"));
83 SCPrint(TRUE, stderr, CFSTR("\t-v\tenable verbose logging\n"));
84 SCPrint(TRUE, stderr, CFSTR("\t-V\tenable verbose logging for the specified plug-in\n"));
85 SCPrint(TRUE, stderr, CFSTR("\t-b\tdisable loading of ALL plug-ins\n"));
86 SCPrint(TRUE, stderr, CFSTR("\t-B\tdisable loading of the specified plug-in\n"));
87 SCPrint(TRUE, stderr, CFSTR("\t-t\tload/test the specified plug-in\n"));
88 SCPrint(TRUE, stderr, CFSTR("\t\t (Note: only the plug-in will be started)\n"));
89 exit (EX_USAGE);
90 }
91
92
93 static void
94 catcher(int signum)
95 {
96 switch (signum) {
97 case SIGTERM :
98 if (termRequested) {
99 mach_msg_empty_send_t msg;
100 mach_msg_option_t options;
101 kern_return_t status;
102
103 /*
104 * send message to indicate that a request has been made
105 * for the daemon to be shutdown.
106 */
107 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
108 msg.header.msgh_size = sizeof(msg);
109 msg.header.msgh_remote_port = CFMachPortGetPort(termRequested);
110 msg.header.msgh_local_port = MACH_PORT_NULL;
111 msg.header.msgh_id = 0;
112 options = MACH_SEND_TIMEOUT;
113 status = mach_msg(&msg.header, /* msg */
114 MACH_SEND_MSG|options, /* options */
115 msg.header.msgh_size, /* send_size */
116 0, /* rcv_size */
117 MACH_PORT_NULL, /* rcv_name */
118 0, /* timeout */
119 MACH_PORT_NULL); /* notify */
120 }
121 break;
122 }
123
124 return;
125 }
126
127
128 static void
129 term(CFMachPortRef port, void *msg, CFIndex size, void *info)
130 {
131 server_shutdown();
132 }
133
134
135 static void
136 parent_exit(int i)
137 {
138 _exit (0);
139 }
140
141
142 static void
143 init_fds()
144 {
145 int fd;
146 int i;
147
148 /* close any open FDs */
149 for (i = getdtablesize()-1; i>=0; i--) close(i);
150
151 /* set stdin, stdout, stderr */
152 fd = open(_PATH_DEVNULL, O_RDWR, 0);
153 if (fd != -1) {
154 int ofd;
155
156 // stdin
157 (void) dup2(fd, STDIN_FILENO);
158
159 // stdout, stderr
160 ofd = open("/var/log/configd.log", O_WRONLY|O_APPEND, 0);
161 if (ofd != -1) {
162 if (fd > STDIN_FILENO) {
163 (void) close(fd);
164 }
165 fd = ofd;
166 }
167 (void) dup2(fd, STDOUT_FILENO);
168 (void) dup2(fd, STDERR_FILENO);
169 if (fd > STDERR_FILENO) {
170 (void) close(fd);
171 }
172 }
173
174 return;
175 }
176
177
178 static void
179 set_trace()
180 {
181 int fd;
182
183 /* set _configd_trace */
184 fd = open("/var/log/configd.trace", O_WRONLY|O_APPEND, 0);
185 if (fd != -1) {
186 _configd_trace = fdopen(fd, "a");
187 }
188
189 return;
190 }
191
192
193 static int
194 fork_child()
195 {
196 int child_pid;
197
198 signal(SIGTERM, parent_exit);
199 child_pid = fork();
200 switch (child_pid) {
201 case -1: {
202 return -1;
203 }
204 case 0: {
205 /* child: becomes the daemon (see below) */
206 signal(SIGTERM, SIG_DFL);
207 break;
208 }
209 default: {
210 /* parent: wait for signal, then exit */
211 int status;
212
213 (void) wait4(child_pid, (int *)&status, 0, 0);
214 if (WIFEXITED(status)) {
215 fprintf(stderr,
216 "*** configd (daemon) failed to start, exit status=%d",
217 WEXITSTATUS(status));
218 } else {
219 fprintf(stderr,
220 "*** configd (daemon) failed to start, received signal=%d",
221 WTERMSIG(status));
222 }
223 fflush (stderr);
224 exit (EX_SOFTWARE);
225 }
226 }
227
228 if (setsid() == -1)
229 return -1;
230
231 (void) chdir("/");
232
233 return 0;
234 }
235
236
237 static void
238 writepid(void)
239 {
240 FILE *fp;
241
242 fp = fopen("/var/run/configd.pid", "w");
243 if (fp != NULL) {
244 fprintf(fp, "%d\n", getpid());
245 fclose(fp);
246 }
247 }
248
249
250 int
251 main(int argc, char * const argv[])
252 {
253 Boolean enableRestart = (argc <= 1); /* only if there are no arguments */
254 Boolean forceForeground = FALSE;
255 Boolean loadBundles = TRUE;
256 struct sigaction nact;
257 int opt;
258 extern int optind;
259 const char *prog = argv[0];
260 mach_port_t service_port = MACH_PORT_NULL;
261 CFStringRef str;
262 const char *testBundle = NULL;
263
264 _plugins_exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
265 _plugins_verbose = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
266
267 /* process any arguments */
268
269 while ((opt = getopt_long(argc, argv, "bB:dt:vV:", longopts, NULL)) != -1) {
270 switch(opt) {
271 case 'b':
272 loadBundles = FALSE;
273 break;
274 case 'B':
275 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
276 CFSetSetValue(_plugins_exclude, str);
277 CFRelease(str);
278 break;
279 case 'd':
280 forceForeground = TRUE;
281 break;
282 case 't':
283 testBundle = optarg;
284 break;
285 case 'v':
286 _configd_verbose = TRUE;
287 break;
288 case 'V':
289 if (strcmp(optarg, "com.apple.SystemConfiguration") == 0) {
290 _sc_verbose = TRUE;
291 } else {
292 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
293 CFSetSetValue(_plugins_verbose, str);
294 CFRelease(str);
295 }
296 break;
297 case '?':
298 default :
299 usage(prog);
300 }
301 }
302 argc -= optind;
303 argv += optind;
304
305 /*
306 * display an error if configd is already running and we are
307 * not solely executing/testing a bundle.
308 */
309 if (testBundle == NULL) {
310 if (server_active(&service_port)) {
311 exit (EX_UNAVAILABLE);
312 }
313 }
314
315 /* check credentials */
316 if (getuid() != 0) {
317 fprintf(stderr, "%s: permission denied.\n", prog);
318 exit (EX_NOPERM);
319 }
320
321 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
322 /*
323 * if we haven't been asked to run in the foreground
324 * and have not been started by mach_init (i.e. we're
325 * not already running as a Foreground process) then
326 * daemonize ourself.
327 */
328 if (fork_child() == -1) {
329 fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
330 exit (1);
331 }
332
333 /*
334 * Note: we are now the child process. The parent
335 * waits/exits in fork_child.
336 */
337 }
338
339 /*
340 * close file descriptors, establish stdin/stdout/stderr,
341 * setup logging.
342 */
343 if (!forceForeground) {
344 int logopt = LOG_NDELAY|LOG_PID;
345
346 init_fds();
347
348 if (_configd_verbose)
349 logopt |= LOG_CONS;
350 openlog("configd", logopt, LOG_DAEMON);
351 } else {
352 _sc_log = FALSE; /* redirect SCLog() to stdout/stderr */
353 }
354
355 /* check/enable trace logging */
356 set_trace();
357
358 /* record process id */
359 if (testBundle == NULL) {
360 writepid();
361 }
362
363 /* add signal handler to catch a SIGHUP */
364 nact.sa_handler = catcher;
365 sigemptyset(&nact.sa_mask);
366 nact.sa_flags = SA_RESTART;
367 if (sigaction(SIGHUP, &nact, NULL) == -1) {
368 SCLog(_configd_verbose, LOG_ERR,
369 CFSTR("sigaction(SIGHUP, ...) failed: %s"),
370 strerror(errno));
371 }
372
373 /* add signal handler to catch a SIGPIPE */
374 if (sigaction(SIGPIPE, &nact, NULL) == -1) {
375 SCLog(_configd_verbose, LOG_ERR,
376 CFSTR("sigaction(SIGPIPE, ...) failed: %s"),
377 strerror(errno));
378 }
379
380 /* add signal handler to catch a SIGTERM (if dynamic store) */
381 if (testBundle == NULL) {
382 if (enableRestart) {
383 mach_port_limits_t limits;
384 CFRunLoopSourceRef rls;
385 kern_return_t status;
386
387 /* add signal handler */
388 if (sigaction(SIGTERM, &nact, NULL) == -1) {
389 SCLog(_configd_verbose, LOG_ERR,
390 CFSTR("sigaction(SIGTERM, ...) failed: %s"),
391 strerror(errno));
392 }
393
394 /* create the "shutdown requested" notification port */
395 termRequested = CFMachPortCreate(NULL, term, NULL, NULL);
396
397 // set queue limit
398 limits.mpl_qlimit = 1;
399 status = mach_port_set_attributes(mach_task_self(),
400 CFMachPortGetPort(termRequested),
401 MACH_PORT_LIMITS_INFO,
402 (mach_port_info_t)&limits,
403 MACH_PORT_LIMITS_INFO_COUNT);
404 if (status != KERN_SUCCESS) {
405 perror("mach_port_set_attributes");
406 }
407
408 // add to our runloop
409 rls = CFMachPortCreateRunLoopSource(NULL, termRequested, 0);
410 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
411 CFRelease(rls);
412 }
413
414 /* initialize primary (store management) thread */
415 server_init(service_port, enableRestart);
416
417 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
418 /* synchronize with parent process */
419 kill(getppid(), SIGTERM);
420 }
421
422 /* load/initialize/start bundles into the secondary thread */
423 if (loadBundles) {
424 objc_setMultithreaded(YES);
425 plugin_init();
426 }
427
428 /* start primary (store management) thread */
429 server_loop();
430 } else {
431 /* load/initialize/start specified plug-in */
432 plugin_exec((void *)testBundle);
433 }
434
435 exit (EX_OK); // insure the process exit status is 0
436 return 0; // ...and make main fit the ANSI spec.
437 }