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