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