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