2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * October 30, 2003 Allan Nathanson <ajn@apple.com>
28 * - add plugin "stop()" function support
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
33 * 24 March 2000 Allan Nathanson (ajn@apple.com)
44 #include <sys/types.h>
46 #include <objc/objc-runtime.h>
49 #include "configd_server.h"
50 #include "plugin_support.h"
53 Boolean _configd_verbose = FALSE; /* TRUE if verbose logging enabled */
56 FILE *_configd_trace = NULL; /* non-NULL if tracing enabled */
59 CFMutableSetRef _plugins_exclude = NULL; /* bundle identifiers to exclude from loading */
62 CFMutableSetRef _plugins_verbose = NULL; /* bundle identifiers to enable verbose logging */
64 static CFMachPortRef termRequested = NULL; /* Mach port used to notify runloop of a shutdown request */
67 static const struct option longopts[] = {
68 // { "no-bundles", no_argument, 0, 'b' },
69 // { "exclude-plugin", required_argument, 0, 'B' },
70 // { "no-fork", no_argument, 0, 'd' },
71 // { "test-bundle", required_argument, 0, 't' },
72 // { "verbose", no_argument, 0, 'v' },
73 // { "verbose-bundle", required_argument, 0, 'V' },
74 { "help", no_argument, 0, '?' },
80 usage(const char *prog)
82 SCPrint(TRUE, stderr, CFSTR("%s: [-d] [-v] [-V bundleID] [-b] [-B bundleID] [-t bundle-path]\n"), prog);
83 SCPrint(TRUE, stderr, CFSTR("options:\n"));
84 SCPrint(TRUE, stderr, CFSTR("\t-d\tdisable daemon/run in foreground\n"));
85 SCPrint(TRUE, stderr, CFSTR("\t-v\tenable verbose logging\n"));
86 SCPrint(TRUE, stderr, CFSTR("\t-V\tenable verbose logging for the specified plug-in\n"));
87 SCPrint(TRUE, stderr, CFSTR("\t-b\tdisable loading of ALL plug-ins\n"));
88 SCPrint(TRUE, stderr, CFSTR("\t-B\tdisable loading of the specified plug-in\n"));
89 SCPrint(TRUE, stderr, CFSTR("\t-t\tload/test the specified plug-in\n"));
90 SCPrint(TRUE, stderr, CFSTR("\t\t (Note: only the plug-in will be started)\n"));
100 if (termRequested != NULL) {
101 mach_msg_empty_send_t msg;
102 mach_msg_option_t options;
103 kern_return_t status;
106 * send message to indicate that a request has been made
107 * for the daemon to be shutdown.
109 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
110 msg.header.msgh_size = sizeof(msg);
111 msg.header.msgh_remote_port = CFMachPortGetPort(termRequested);
112 msg.header.msgh_local_port = MACH_PORT_NULL;
113 msg.header.msgh_id = 0;
114 options = MACH_SEND_TIMEOUT;
115 status = mach_msg(&msg.header, /* msg */
116 MACH_SEND_MSG|options, /* options */
117 msg.header.msgh_size, /* send_size */
119 MACH_PORT_NULL, /* rcv_name */
121 MACH_PORT_NULL); /* notify */
122 if (status == MACH_SEND_TIMED_OUT) {
123 mach_msg_destroy(&msg.header);
136 term(CFMachPortRef port, void *msg, CFIndex size, void *info)
141 wait = plugin_term(&status);
143 // if we are not waiting on a plugin
144 status = server_shutdown();
165 /* close any open FDs */
166 for (i = getdtablesize()-1; i>=0; i--) close(i);
168 /* set stdin, stdout, stderr */
169 fd = open(_PATH_DEVNULL, O_RDWR, 0);
174 (void) dup2(fd, STDIN_FILENO);
177 ofd = open("/var/log/configd.log", O_WRONLY|O_APPEND, 0);
179 if (fd > STDIN_FILENO) {
184 (void) dup2(fd, STDOUT_FILENO);
185 (void) dup2(fd, STDERR_FILENO);
186 if (fd > STDERR_FILENO) {
200 /* set _configd_trace */
201 fd = open("/var/log/configd.trace", O_WRONLY|O_APPEND, 0);
203 _configd_trace = fdopen(fd, "a");
215 signal(SIGTERM, parent_exit);
222 /* child: becomes the daemon (see below) */
223 signal(SIGTERM, SIG_DFL);
227 /* parent: wait for signal, then exit */
230 (void) wait4(child_pid, (int *)&status, 0, 0);
231 if (WIFEXITED(status)) {
233 "*** configd (daemon) failed to start, exit status=%d",
234 WEXITSTATUS(status));
237 "*** configd (daemon) failed to start, received signal=%d",
259 fp = fopen("/var/run/configd.pid", "w");
261 fprintf(fp, "%d\n", getpid());
268 main(int argc, char * const argv[])
270 Boolean enableRestart = (argc <= 1); /* only if there are no arguments */
271 Boolean forceForeground = FALSE;
272 mach_port_limits_t limits;
273 Boolean loadBundles = TRUE;
274 struct sigaction nact;
277 const char *prog = argv[0];
278 CFRunLoopSourceRef rls;
279 mach_port_t service_port = MACH_PORT_NULL;
280 kern_return_t status;
282 const char *testBundle = NULL;
284 _plugins_exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
285 _plugins_verbose = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
287 /* process any arguments */
289 while ((opt = getopt_long(argc, argv, "bB:dt:vV:", longopts, NULL)) != -1) {
295 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
296 CFSetSetValue(_plugins_exclude, str);
300 forceForeground = TRUE;
306 _configd_verbose = TRUE;
309 if (strcmp(optarg, "com.apple.SystemConfiguration") == 0) {
312 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
313 CFSetSetValue(_plugins_verbose, str);
326 * display an error if configd is already running and we are
327 * not solely executing/testing a bundle.
329 if (testBundle == NULL) {
330 if (server_active(&service_port)) {
331 exit (EX_UNAVAILABLE);
335 /* check credentials */
337 fprintf(stderr, "%s: permission denied.\n", prog);
341 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
343 * if we haven't been asked to run in the foreground
344 * and have not been started by mach_init (i.e. we're
345 * not already running as a Foreground process) then
348 if (fork_child() == -1) {
349 fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
354 * Note: we are now the child process. The parent
355 * waits/exits in fork_child.
360 * close file descriptors, establish stdin/stdout/stderr,
363 if (!forceForeground) {
364 int logopt = LOG_NDELAY|LOG_PID;
368 if (_configd_verbose)
370 openlog("configd", logopt, LOG_DAEMON);
372 _sc_log = FALSE; /* redirect SCLog() to stdout/stderr */
375 /* check/enable trace logging */
378 /* record process id */
379 if (testBundle == NULL) {
383 /* add signal handler to catch a SIGHUP */
384 nact.sa_handler = catcher;
385 sigemptyset(&nact.sa_mask);
386 nact.sa_flags = SA_RESTART;
387 if (sigaction(SIGHUP, &nact, NULL) == -1) {
388 SCLog(_configd_verbose, LOG_ERR,
389 CFSTR("sigaction(SIGHUP, ...) failed: %s"),
393 /* add signal handler to catch a SIGPIPE */
394 if (sigaction(SIGPIPE, &nact, NULL) == -1) {
395 SCLog(_configd_verbose, LOG_ERR,
396 CFSTR("sigaction(SIGPIPE, ...) failed: %s"),
400 /* add signal handler to catch a SIGTERM */
401 if (sigaction(SIGTERM, &nact, NULL) == -1) {
402 SCLog(_configd_verbose, LOG_ERR,
403 CFSTR("sigaction(SIGTERM, ...) failed: %s"),
407 /* create the "shutdown requested" notification port */
408 termRequested = CFMachPortCreate(NULL, term, NULL, NULL);
411 limits.mpl_qlimit = 1;
412 status = mach_port_set_attributes(mach_task_self(),
413 CFMachPortGetPort(termRequested),
414 MACH_PORT_LIMITS_INFO,
415 (mach_port_info_t)&limits,
416 MACH_PORT_LIMITS_INFO_COUNT);
417 if (status != KERN_SUCCESS) {
418 perror("mach_port_set_attributes");
421 // add to our runloop
422 rls = CFMachPortCreateRunLoopSource(NULL, termRequested, 0);
423 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
426 if (testBundle == NULL) {
427 /* initialize primary (store management) thread */
428 server_init(service_port, enableRestart);
430 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
431 /* synchronize with parent process */
432 kill(getppid(), SIGTERM);
435 /* load/initialize/start bundles into the secondary thread */
437 objc_setMultithreaded(YES);
441 /* start primary (store management) thread */
444 /* load/initialize/start specified plug-in */
445 plugin_exec((void *)testBundle);
448 exit (EX_OK); // insure the process exit status is 0
449 return 0; // ...and make main fit the ANSI spec.