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