2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
27 * Modification History
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
32 * 24 March 2000 Allan Nathanson (ajn@apple.com)
43 #include <sys/types.h>
45 #include <objc/objc-runtime.h>
48 #include "configd_server.h"
49 #include "plugin_support.h"
51 Boolean _configd_verbose = FALSE; /* TRUE if verbose logging enabled */
54 FILE *_configd_trace = NULL; /* non-NULL if tracing enabled */
57 CFMutableSetRef _plugins_exclude = NULL; /* bundle identifiers to exclude from loading */
60 CFMutableSetRef _plugins_verbose = NULL; /* bundle identifiers to enable verbose logging */
62 static CFMachPortRef termRequested = NULL; /* Mach port used to notify runloop of a shutdown request */
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, '?' },
78 usage(const char *prog)
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"));
99 mach_msg_empty_send_t msg;
100 mach_msg_option_t options;
101 kern_return_t status;
104 * send message to indicate that a request has been made
105 * for the daemon to be shutdown.
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 */
117 MACH_PORT_NULL, /* rcv_name */
119 MACH_PORT_NULL); /* notify */
129 term(CFMachPortRef port, void *msg, CFIndex size, void *info)
148 /* close any open FDs */
149 for (i = getdtablesize()-1; i>=0; i--) close(i);
151 /* set stdin, stdout, stderr */
152 fd = open(_PATH_DEVNULL, O_RDWR, 0);
157 (void) dup2(fd, STDIN_FILENO);
160 ofd = open("/var/log/configd.log", O_WRONLY|O_APPEND, 0);
162 if (fd > STDIN_FILENO) {
167 (void) dup2(fd, STDOUT_FILENO);
168 (void) dup2(fd, STDERR_FILENO);
169 if (fd > STDERR_FILENO) {
183 /* set _configd_trace */
184 fd = open("/var/log/configd.trace", O_WRONLY|O_APPEND, 0);
186 _configd_trace = fdopen(fd, "a");
198 signal(SIGTERM, parent_exit);
205 /* child: becomes the daemon (see below) */
206 signal(SIGTERM, SIG_DFL);
210 /* parent: wait for signal, then exit */
213 (void) wait4(child_pid, (int *)&status, 0, 0);
214 if (WIFEXITED(status)) {
216 "*** configd (daemon) failed to start, exit status=%d",
217 WEXITSTATUS(status));
220 "*** configd (daemon) failed to start, received signal=%d",
242 fp = fopen("/var/run/configd.pid", "w");
244 fprintf(fp, "%d\n", getpid());
251 main(int argc, char * const argv[])
253 Boolean enableRestart = (argc <= 1); /* only if there are no arguments */
254 Boolean forceForeground = FALSE;
255 Boolean loadBundles = TRUE;
256 struct sigaction nact;
259 const char *prog = argv[0];
260 mach_port_t service_port = MACH_PORT_NULL;
262 const char *testBundle = NULL;
264 _plugins_exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
265 _plugins_verbose = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
267 /* process any arguments */
269 while ((opt = getopt_long(argc, argv, "bB:dt:vV:", longopts, NULL)) != -1) {
275 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
276 CFSetSetValue(_plugins_exclude, str);
280 forceForeground = TRUE;
286 _configd_verbose = TRUE;
289 if (strcmp(optarg, "com.apple.SystemConfiguration") == 0) {
292 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
293 CFSetSetValue(_plugins_verbose, str);
306 * display an error if configd is already running and we are
307 * not solely executing/testing a bundle.
309 if (testBundle == NULL) {
310 if (server_active(&service_port)) {
311 exit (EX_UNAVAILABLE);
315 /* check credentials */
317 fprintf(stderr, "%s: permission denied.\n", prog);
321 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
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
328 if (fork_child() == -1) {
329 fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
334 * Note: we are now the child process. The parent
335 * waits/exits in fork_child.
340 * close file descriptors, establish stdin/stdout/stderr,
343 if (!forceForeground) {
344 int logopt = LOG_NDELAY|LOG_PID;
348 if (_configd_verbose)
350 openlog("configd", logopt, LOG_DAEMON);
352 _sc_log = FALSE; /* redirect SCLog() to stdout/stderr */
355 /* check/enable trace logging */
358 /* record process id */
359 if (testBundle == NULL) {
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"),
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"),
380 /* add signal handler to catch a SIGTERM (if dynamic store) */
381 if (testBundle == NULL) {
383 mach_port_limits_t limits;
384 CFRunLoopSourceRef rls;
385 kern_return_t status;
387 /* add signal handler */
388 if (sigaction(SIGTERM, &nact, NULL) == -1) {
389 SCLog(_configd_verbose, LOG_ERR,
390 CFSTR("sigaction(SIGTERM, ...) failed: %s"),
394 /* create the "shutdown requested" notification port */
395 termRequested = CFMachPortCreate(NULL, term, NULL, NULL);
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");
408 // add to our runloop
409 rls = CFMachPortCreateRunLoopSource(NULL, termRequested, 0);
410 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
414 /* initialize primary (store management) thread */
415 server_init(service_port, enableRestart);
417 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
418 /* synchronize with parent process */
419 kill(getppid(), SIGTERM);
422 /* load/initialize/start bundles into the secondary thread */
424 objc_setMultithreaded(YES);
428 /* start primary (store management) thread */
431 /* load/initialize/start specified plug-in */
432 plugin_exec((void *)testBundle);
435 exit (EX_OK); // insure the process exit status is 0
436 return 0; // ...and make main fit the ANSI spec.