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