]> git.saurik.com Git - apple/configd.git/blame - configd.tproj/configd.m
configd-204.tar.gz
[apple/configd.git] / configd.tproj / configd.m
CommitLineData
5958d7c0 1/*
edebe297 2 * Copyright (c) 2000-2007 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 *
dbf6a266
A
27 * October 30, 2003 Allan Nathanson <ajn@apple.com>
28 * - add plugin "stop()" function support
29 *
0fae82ee
A
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
32 *
edebe297 33 * 24 March 2000 Allan Nathanson <ajn@apple.com>
0fae82ee 34 * - created
5958d7c0
A
35 */
36
009ee3c6 37#include <getopt.h>
5958d7c0
A
38#include <stdio.h>
39#include <sysexits.h>
40#include <syslog.h>
41#include <unistd.h>
42#include <paths.h>
43#include <fcntl.h>
44#include <sys/types.h>
edebe297 45#include <sys/stat.h>
5958d7c0
A
46#include <sys/wait.h>
47#include <objc/objc-runtime.h>
edebe297 48#include <servers/bootstrap.h>
5958d7c0
A
49
50#include "configd.h"
51#include "configd_server.h"
52#include "plugin_support.h"
53
dbf6a266 54__private_extern__
a5f60add 55Boolean _configd_verbose = FALSE; /* TRUE if verbose logging enabled */
009ee3c6
A
56
57__private_extern__
58FILE *_configd_trace = NULL; /* non-NULL if tracing enabled */
59
60__private_extern__
0fae82ee 61CFMutableSetRef _plugins_exclude = NULL; /* bundle identifiers to exclude from loading */
009ee3c6
A
62
63__private_extern__
0fae82ee 64CFMutableSetRef _plugins_verbose = NULL; /* bundle identifiers to enable verbose logging */
5958d7c0 65
009ee3c6
A
66static CFMachPortRef termRequested = NULL; /* Mach port used to notify runloop of a shutdown request */
67
68
dbf6a266 69static const struct option longopts[] = {
009ee3c6
A
70// { "no-bundles", no_argument, 0, 'b' },
71// { "exclude-plugin", required_argument, 0, 'B' },
72// { "no-fork", no_argument, 0, 'd' },
73// { "test-bundle", required_argument, 0, 't' },
74// { "verbose", no_argument, 0, 'v' },
75// { "verbose-bundle", required_argument, 0, 'V' },
76 { "help", no_argument, 0, '?' },
77 { 0, 0, 0, 0 }
5958d7c0
A
78};
79
80
0fae82ee 81static void
5958d7c0
A
82usage(const char *prog)
83{
dbf6a266 84 SCPrint(TRUE, stderr, CFSTR("%s: [-d] [-v] [-V bundleID] [-b] [-B bundleID] [-t bundle-path]\n"), prog);
0fae82ee
A
85 SCPrint(TRUE, stderr, CFSTR("options:\n"));
86 SCPrint(TRUE, stderr, CFSTR("\t-d\tdisable daemon/run in foreground\n"));
87 SCPrint(TRUE, stderr, CFSTR("\t-v\tenable verbose logging\n"));
88 SCPrint(TRUE, stderr, CFSTR("\t-V\tenable verbose logging for the specified plug-in\n"));
89 SCPrint(TRUE, stderr, CFSTR("\t-b\tdisable loading of ALL plug-ins\n"));
90 SCPrint(TRUE, stderr, CFSTR("\t-B\tdisable loading of the specified plug-in\n"));
91 SCPrint(TRUE, stderr, CFSTR("\t-t\tload/test the specified plug-in\n"));
009ee3c6 92 SCPrint(TRUE, stderr, CFSTR("\t\t (Note: only the plug-in will be started)\n"));
5958d7c0
A
93 exit (EX_USAGE);
94}
95
96
edebe297
A
97static void
98allow_crash_reports(void)
99{
100 mach_msg_type_number_t i;
101 exception_mask_t masks[EXC_TYPES_COUNT];
102 mach_msg_type_number_t n_masks = 0;
103 mach_port_t new_exception_port = MACH_PORT_NULL;
104 exception_port_t old_handlers[EXC_TYPES_COUNT];
105 exception_behavior_t old_behaviors[EXC_TYPES_COUNT];
106 thread_state_flavor_t old_flavors[EXC_TYPES_COUNT];
107 kern_return_t status;
108
109 status = bootstrap_look_up(bootstrap_port, "com.apple.ReportCrash.DirectoryService", &new_exception_port);
110 if (status != BOOTSTRAP_SUCCESS) {
111 SCLog(TRUE, LOG_ERR,
112 CFSTR("allow_crash_reports bootstrap_look_up() failed: %s"),
113 bootstrap_strerror(status));
114 return;
115 }
116
117 // get information about the original crash exception port for the task
118 status = task_get_exception_ports(mach_task_self(),
119 EXC_MASK_CRASH,
120 masks,
121 &n_masks,
122 old_handlers,
123 old_behaviors,
124 old_flavors);
125 if (status != KERN_SUCCESS) {
126 SCLog(TRUE, LOG_ERR,
127 CFSTR("allow_crash_reports task_get_exception_ports() failed: %s"),
128 mach_error_string(status));
129 return;
130 }
131
132 // replace the original crash exception port with our new port
133 for (i = 0; i < n_masks; i++) {
134 status = task_set_exception_ports(mach_task_self(),
135 masks[i],
136 new_exception_port,
137 old_behaviors[i],
138 old_flavors[i]);
139 if (status != KERN_SUCCESS) {
140 SCLog(TRUE, LOG_ERR,
141 CFSTR("allow_crash_reports task_set_exception_ports() failed: %s"),
142 mach_error_string(status));
143 }
144 }
145
146 return;
147}
148
149
0fae82ee 150static void
5958d7c0
A
151catcher(int signum)
152{
009ee3c6 153 switch (signum) {
edebe297 154 case SIGINT :
009ee3c6 155 case SIGTERM :
dbf6a266 156 if (termRequested != NULL) {
edebe297
A
157 if (_sc_log > 0) {
158 /*
159 * if we've received a [shutdown] SIGTERM
160 * and we are syslog'ing than it's likely
161 * that syslogd is also being term'd. As
162 * such, let's also push any remaining log
163 * messages to stdout/stderr.
164 */
165 _sc_log++;
166 }
009ee3c6
A
167
168 /*
169 * send message to indicate that a request has been made
170 * for the daemon to be shutdown.
171 */
edebe297 172 _SC_sendMachMessage(CFMachPortGetPort(termRequested), 0);
dbf6a266
A
173 } else {
174 _exit(EX_OK);
009ee3c6
A
175 }
176 break;
5958d7c0
A
177 }
178
179 return;
180}
181
009ee3c6
A
182
183static void
184term(CFMachPortRef port, void *msg, CFIndex size, void *info)
185{
dbf6a266
A
186 int status = EX_OK;
187 Boolean wait;
188
189 wait = plugin_term(&status);
190 if (!wait) {
191 // if we are not waiting on a plugin
192 status = server_shutdown();
193 exit (status);
194 }
195
196 return;
009ee3c6
A
197}
198
199
5958d7c0
A
200static void
201parent_exit(int i)
202{
203 _exit (0);
204}
205
009ee3c6
A
206
207static void
208init_fds()
209{
210 int fd;
211 int i;
212
213 /* close any open FDs */
214 for (i = getdtablesize()-1; i>=0; i--) close(i);
215
216 /* set stdin, stdout, stderr */
217 fd = open(_PATH_DEVNULL, O_RDWR, 0);
218 if (fd != -1) {
219 int ofd;
220
221 // stdin
222 (void) dup2(fd, STDIN_FILENO);
223
224 // stdout, stderr
225 ofd = open("/var/log/configd.log", O_WRONLY|O_APPEND, 0);
226 if (ofd != -1) {
227 if (fd > STDIN_FILENO) {
228 (void) close(fd);
229 }
230 fd = ofd;
231 }
232 (void) dup2(fd, STDOUT_FILENO);
233 (void) dup2(fd, STDERR_FILENO);
234 if (fd > STDERR_FILENO) {
235 (void) close(fd);
236 }
237 }
238
edebe297 239 SCTrace(TRUE, stdout, CFSTR("start\n"));
009ee3c6
A
240 return;
241}
242
243
244static void
245set_trace()
246{
247 int fd;
248
249 /* set _configd_trace */
250 fd = open("/var/log/configd.trace", O_WRONLY|O_APPEND, 0);
251 if (fd != -1) {
252 _configd_trace = fdopen(fd, "a");
edebe297 253 SCTrace(TRUE, _configd_trace, CFSTR("start\n"));
009ee3c6
A
254 }
255
256 return;
257}
258
259
5958d7c0
A
260static int
261fork_child()
262{
263 int child_pid;
5958d7c0
A
264
265 signal(SIGTERM, parent_exit);
266 child_pid = fork();
267 switch (child_pid) {
268 case -1: {
269 return -1;
270 }
271 case 0: {
272 /* child: becomes the daemon (see below) */
273 signal(SIGTERM, SIG_DFL);
274 break;
275 }
276 default: {
277 /* parent: wait for signal, then exit */
278 int status;
279
280 (void) wait4(child_pid, (int *)&status, 0, 0);
281 if (WIFEXITED(status)) {
282 fprintf(stderr,
283 "*** configd (daemon) failed to start, exit status=%d",
284 WEXITSTATUS(status));
285 } else {
286 fprintf(stderr,
287 "*** configd (daemon) failed to start, received signal=%d",
288 WTERMSIG(status));
289 }
290 fflush (stderr);
291 exit (EX_SOFTWARE);
292 }
293 }
294
295 if (setsid() == -1)
296 return -1;
297
0fae82ee 298 (void) chdir("/");
5958d7c0 299
5958d7c0
A
300 return 0;
301}
302
a5f60add
A
303
304static void
305writepid(void)
306{
307 FILE *fp;
308
309 fp = fopen("/var/run/configd.pid", "w");
310 if (fp != NULL) {
311 fprintf(fp, "%d\n", getpid());
312 fclose(fp);
313 }
314}
315
316
edebe297
A
317static CFStringRef
318termMPCopyDescription(const void *info)
319{
320 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGTERM MP>"));
321}
322
323
5958d7c0 324int
a5f60add 325main(int argc, char * const argv[])
5958d7c0 326{
edebe297
A
327 CFMachPortContext context = { 0
328 , (void *)1
329 , NULL
330 , NULL
331 , termMPCopyDescription
332 };
009ee3c6
A
333 Boolean enableRestart = (argc <= 1); /* only if there are no arguments */
334 Boolean forceForeground = FALSE;
dbf6a266 335 mach_port_limits_t limits;
009ee3c6 336 Boolean loadBundles = TRUE;
0fae82ee 337 struct sigaction nact;
5958d7c0 338 int opt;
0fae82ee 339 extern int optind;
009ee3c6 340 const char *prog = argv[0];
dbf6a266 341 CFRunLoopSourceRef rls;
009ee3c6 342 mach_port_t service_port = MACH_PORT_NULL;
dbf6a266 343 kern_return_t status;
0fae82ee 344 CFStringRef str;
009ee3c6 345 const char *testBundle = NULL;
0fae82ee
A
346
347 _plugins_exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
348 _plugins_verbose = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
5958d7c0
A
349
350 /* process any arguments */
351
009ee3c6 352 while ((opt = getopt_long(argc, argv, "bB:dt:vV:", longopts, NULL)) != -1) {
5958d7c0
A
353 switch(opt) {
354 case 'b':
355 loadBundles = FALSE;
356 break;
0fae82ee
A
357 case 'B':
358 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
359 CFSetSetValue(_plugins_exclude, str);
360 CFRelease(str);
361 break;
5958d7c0 362 case 'd':
009ee3c6 363 forceForeground = TRUE;
5958d7c0
A
364 break;
365 case 't':
366 testBundle = optarg;
367 break;
368 case 'v':
0fae82ee
A
369 _configd_verbose = TRUE;
370 break;
371 case 'V':
372 if (strcmp(optarg, "com.apple.SystemConfiguration") == 0) {
373 _sc_verbose = TRUE;
374 } else {
009ee3c6
A
375 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
376 CFSetSetValue(_plugins_verbose, str);
377 CFRelease(str);
0fae82ee 378 }
5958d7c0
A
379 break;
380 case '?':
381 default :
382 usage(prog);
383 }
384 }
385 argc -= optind;
386 argv += optind;
387
388 /*
389 * display an error if configd is already running and we are
009ee3c6 390 * not solely executing/testing a bundle.
5958d7c0 391 */
009ee3c6
A
392 if (testBundle == NULL) {
393 if (server_active(&service_port)) {
394 exit (EX_UNAVAILABLE);
395 }
5958d7c0
A
396 }
397
5958d7c0
A
398 /* check credentials */
399 if (getuid() != 0) {
0fae82ee
A
400 fprintf(stderr, "%s: permission denied.\n", prog);
401 exit (EX_NOPERM);
5958d7c0
A
402 }
403
009ee3c6
A
404 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
405 /*
406 * if we haven't been asked to run in the foreground
edebe297 407 * and have not been started by launchd (i.e. we're
009ee3c6
A
408 * not already running as a Foreground process) then
409 * daemonize ourself.
410 */
5958d7c0
A
411 if (fork_child() == -1) {
412 fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
413 exit (1);
414 }
0fae82ee 415
009ee3c6
A
416 /*
417 * Note: we are now the child process. The parent
418 * waits/exits in fork_child.
419 */
a5f60add
A
420 }
421
009ee3c6
A
422 /*
423 * close file descriptors, establish stdin/stdout/stderr,
424 * setup logging.
425 */
426 if (!forceForeground) {
edebe297
A
427 int facility = LOG_DAEMON;
428 int logopt = LOG_CONS|LOG_NDELAY|LOG_PID;
429 struct stat statbuf;
5958d7c0 430
edebe297
A
431 if (service_port == MACH_PORT_NULL) {
432 init_fds();
433 }
009ee3c6 434
edebe297 435 if (_configd_verbose) {
0fae82ee 436 logopt |= LOG_CONS;
edebe297
A
437 }
438
439 if (stat("/etc/rc.cdrom", &statbuf) == 0) {
440 facility = LOG_INSTALL;
441 }
442
443 openlog("configd", logopt, facility);
0fae82ee
A
444 } else {
445 _sc_log = FALSE; /* redirect SCLog() to stdout/stderr */
5958d7c0
A
446 }
447
edebe297
A
448 /* enable crash reporting */
449 allow_crash_reports();
450
009ee3c6
A
451 /* check/enable trace logging */
452 set_trace();
5958d7c0 453
009ee3c6
A
454 /* record process id */
455 if (testBundle == NULL) {
456 writepid();
457 }
458
459 /* add signal handler to catch a SIGHUP */
5958d7c0
A
460 nact.sa_handler = catcher;
461 sigemptyset(&nact.sa_mask);
462 nact.sa_flags = SA_RESTART;
0fae82ee
A
463 if (sigaction(SIGHUP, &nact, NULL) == -1) {
464 SCLog(_configd_verbose, LOG_ERR,
465 CFSTR("sigaction(SIGHUP, ...) failed: %s"),
466 strerror(errno));
467 }
468
469 /* add signal handler to catch a SIGPIPE */
5958d7c0 470 if (sigaction(SIGPIPE, &nact, NULL) == -1) {
0fae82ee 471 SCLog(_configd_verbose, LOG_ERR,
5958d7c0
A
472 CFSTR("sigaction(SIGPIPE, ...) failed: %s"),
473 strerror(errno));
474 }
475
dbf6a266
A
476 /* add signal handler to catch a SIGTERM */
477 if (sigaction(SIGTERM, &nact, NULL) == -1) {
478 SCLog(_configd_verbose, LOG_ERR,
479 CFSTR("sigaction(SIGTERM, ...) failed: %s"),
480 strerror(errno));
481 }
5958d7c0 482
edebe297
A
483 /* add signal handler to catch a SIGINT */
484 if (sigaction(SIGINT, &nact, NULL) == -1) {
485 SCLog(_configd_verbose, LOG_ERR,
486 CFSTR("sigaction(SIGINT, ...) failed: %s"),
487 strerror(errno));
488 }
489
dbf6a266 490 /* create the "shutdown requested" notification port */
edebe297 491 termRequested = CFMachPortCreate(NULL, term, &context, NULL);
dbf6a266 492
edebe297 493 /* set queue limit */
dbf6a266
A
494 limits.mpl_qlimit = 1;
495 status = mach_port_set_attributes(mach_task_self(),
496 CFMachPortGetPort(termRequested),
497 MACH_PORT_LIMITS_INFO,
498 (mach_port_info_t)&limits,
499 MACH_PORT_LIMITS_INFO_COUNT);
500 if (status != KERN_SUCCESS) {
501 perror("mach_port_set_attributes");
502 }
009ee3c6 503
edebe297 504 /* add to our runloop */
dbf6a266
A
505 rls = CFMachPortCreateRunLoopSource(NULL, termRequested, 0);
506 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
507 CFRelease(rls);
5958d7c0 508
dbf6a266 509 if (testBundle == NULL) {
0fae82ee 510 /* initialize primary (store management) thread */
009ee3c6
A
511 server_init(service_port, enableRestart);
512
513 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
edebe297
A
514 /* synchronize with parent process */
515 kill(getppid(), SIGTERM);
009ee3c6 516 }
5958d7c0
A
517
518 /* load/initialize/start bundles into the secondary thread */
519 if (loadBundles) {
edebe297 520 /* start plug-in initialization */
5958d7c0 521 plugin_init();
5958d7c0 522 }
5958d7c0 523
0fae82ee 524 /* start primary (store management) thread */
5958d7c0
A
525 server_loop();
526 } else {
527 /* load/initialize/start specified plug-in */
528 plugin_exec((void *)testBundle);
529 }
530
edebe297
A
531 exit (EX_OK); /* insure the process exit status is 0 */
532 return 0; /* ...and make main fit the ANSI spec. */
5958d7c0 533}