]> git.saurik.com Git - apple/configd.git/blame - configd.tproj/configd.m
configd-137.3.tar.gz
[apple/configd.git] / configd.tproj / configd.m
CommitLineData
5958d7c0 1/*
dbf6a266 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
5958d7c0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
009ee3c6 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.
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
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.
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 *
33 * 24 March 2000 Allan Nathanson (ajn@apple.com)
34 * - created
5958d7c0
A
35 */
36
009ee3c6 37#include <getopt.h>
5958d7c0
A
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/wait.h>
46#include <objc/objc-runtime.h>
47
48#include "configd.h"
49#include "configd_server.h"
50#include "plugin_support.h"
51
dbf6a266 52__private_extern__
a5f60add 53Boolean _configd_verbose = FALSE; /* TRUE if verbose logging enabled */
009ee3c6
A
54
55__private_extern__
56FILE *_configd_trace = NULL; /* non-NULL if tracing enabled */
57
58__private_extern__
0fae82ee 59CFMutableSetRef _plugins_exclude = NULL; /* bundle identifiers to exclude from loading */
009ee3c6
A
60
61__private_extern__
0fae82ee 62CFMutableSetRef _plugins_verbose = NULL; /* bundle identifiers to enable verbose logging */
5958d7c0 63
009ee3c6
A
64static CFMachPortRef termRequested = NULL; /* Mach port used to notify runloop of a shutdown request */
65
66
dbf6a266 67static const struct option longopts[] = {
009ee3c6
A
68// { "no-bundles", no_argument, 0, 'b' },
69// { "exclude-plugin", required_argument, 0, 'B' },
70// { "no-fork", no_argument, 0, 'd' },
71// { "test-bundle", required_argument, 0, 't' },
72// { "verbose", no_argument, 0, 'v' },
73// { "verbose-bundle", required_argument, 0, 'V' },
74 { "help", no_argument, 0, '?' },
75 { 0, 0, 0, 0 }
5958d7c0
A
76};
77
78
0fae82ee 79static void
5958d7c0
A
80usage(const char *prog)
81{
dbf6a266 82 SCPrint(TRUE, stderr, CFSTR("%s: [-d] [-v] [-V bundleID] [-b] [-B bundleID] [-t bundle-path]\n"), prog);
0fae82ee
A
83 SCPrint(TRUE, stderr, CFSTR("options:\n"));
84 SCPrint(TRUE, stderr, CFSTR("\t-d\tdisable daemon/run in foreground\n"));
85 SCPrint(TRUE, stderr, CFSTR("\t-v\tenable verbose logging\n"));
86 SCPrint(TRUE, stderr, CFSTR("\t-V\tenable verbose logging for the specified plug-in\n"));
87 SCPrint(TRUE, stderr, CFSTR("\t-b\tdisable loading of ALL plug-ins\n"));
88 SCPrint(TRUE, stderr, CFSTR("\t-B\tdisable loading of the specified plug-in\n"));
89 SCPrint(TRUE, stderr, CFSTR("\t-t\tload/test the specified plug-in\n"));
009ee3c6 90 SCPrint(TRUE, stderr, CFSTR("\t\t (Note: only the plug-in will be started)\n"));
5958d7c0
A
91 exit (EX_USAGE);
92}
93
94
0fae82ee 95static void
5958d7c0
A
96catcher(int signum)
97{
009ee3c6
A
98 switch (signum) {
99 case SIGTERM :
dbf6a266 100 if (termRequested != NULL) {
009ee3c6
A
101 mach_msg_empty_send_t msg;
102 mach_msg_option_t options;
103 kern_return_t status;
104
105 /*
106 * send message to indicate that a request has been made
107 * for the daemon to be shutdown.
108 */
109 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
110 msg.header.msgh_size = sizeof(msg);
111 msg.header.msgh_remote_port = CFMachPortGetPort(termRequested);
112 msg.header.msgh_local_port = MACH_PORT_NULL;
113 msg.header.msgh_id = 0;
114 options = MACH_SEND_TIMEOUT;
115 status = mach_msg(&msg.header, /* msg */
116 MACH_SEND_MSG|options, /* options */
117 msg.header.msgh_size, /* send_size */
118 0, /* rcv_size */
119 MACH_PORT_NULL, /* rcv_name */
120 0, /* timeout */
121 MACH_PORT_NULL); /* notify */
dbf6a266
A
122 if (status == MACH_SEND_TIMED_OUT) {
123 mach_msg_destroy(&msg.header);
124 }
125 } else {
126 _exit(EX_OK);
009ee3c6
A
127 }
128 break;
5958d7c0
A
129 }
130
131 return;
132}
133
009ee3c6
A
134
135static void
136term(CFMachPortRef port, void *msg, CFIndex size, void *info)
137{
dbf6a266
A
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;
009ee3c6
A
149}
150
151
5958d7c0
A
152static void
153parent_exit(int i)
154{
155 _exit (0);
156}
157
009ee3c6
A
158
159static void
160init_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 return;
192}
193
194
195static void
196set_trace()
197{
198 int fd;
199
200 /* set _configd_trace */
201 fd = open("/var/log/configd.trace", O_WRONLY|O_APPEND, 0);
202 if (fd != -1) {
203 _configd_trace = fdopen(fd, "a");
204 }
205
206 return;
207}
208
209
5958d7c0
A
210static int
211fork_child()
212{
213 int child_pid;
5958d7c0
A
214
215 signal(SIGTERM, parent_exit);
216 child_pid = fork();
217 switch (child_pid) {
218 case -1: {
219 return -1;
220 }
221 case 0: {
222 /* child: becomes the daemon (see below) */
223 signal(SIGTERM, SIG_DFL);
224 break;
225 }
226 default: {
227 /* parent: wait for signal, then exit */
228 int status;
229
230 (void) wait4(child_pid, (int *)&status, 0, 0);
231 if (WIFEXITED(status)) {
232 fprintf(stderr,
233 "*** configd (daemon) failed to start, exit status=%d",
234 WEXITSTATUS(status));
235 } else {
236 fprintf(stderr,
237 "*** configd (daemon) failed to start, received signal=%d",
238 WTERMSIG(status));
239 }
240 fflush (stderr);
241 exit (EX_SOFTWARE);
242 }
243 }
244
245 if (setsid() == -1)
246 return -1;
247
0fae82ee 248 (void) chdir("/");
5958d7c0 249
5958d7c0
A
250 return 0;
251}
252
a5f60add
A
253
254static void
255writepid(void)
256{
257 FILE *fp;
258
259 fp = fopen("/var/run/configd.pid", "w");
260 if (fp != NULL) {
261 fprintf(fp, "%d\n", getpid());
262 fclose(fp);
263 }
264}
265
266
5958d7c0 267int
a5f60add 268main(int argc, char * const argv[])
5958d7c0 269{
009ee3c6
A
270 Boolean enableRestart = (argc <= 1); /* only if there are no arguments */
271 Boolean forceForeground = FALSE;
dbf6a266 272 mach_port_limits_t limits;
009ee3c6 273 Boolean loadBundles = TRUE;
0fae82ee 274 struct sigaction nact;
5958d7c0 275 int opt;
0fae82ee 276 extern int optind;
009ee3c6 277 const char *prog = argv[0];
dbf6a266 278 CFRunLoopSourceRef rls;
009ee3c6 279 mach_port_t service_port = MACH_PORT_NULL;
dbf6a266 280 kern_return_t status;
0fae82ee 281 CFStringRef str;
009ee3c6 282 const char *testBundle = NULL;
0fae82ee
A
283
284 _plugins_exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
285 _plugins_verbose = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
5958d7c0
A
286
287 /* process any arguments */
288
009ee3c6 289 while ((opt = getopt_long(argc, argv, "bB:dt:vV:", longopts, NULL)) != -1) {
5958d7c0
A
290 switch(opt) {
291 case 'b':
292 loadBundles = FALSE;
293 break;
0fae82ee
A
294 case 'B':
295 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
296 CFSetSetValue(_plugins_exclude, str);
297 CFRelease(str);
298 break;
5958d7c0 299 case 'd':
009ee3c6 300 forceForeground = TRUE;
5958d7c0
A
301 break;
302 case 't':
303 testBundle = optarg;
304 break;
305 case 'v':
0fae82ee
A
306 _configd_verbose = TRUE;
307 break;
308 case 'V':
309 if (strcmp(optarg, "com.apple.SystemConfiguration") == 0) {
310 _sc_verbose = TRUE;
311 } else {
009ee3c6
A
312 str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingMacRoman);
313 CFSetSetValue(_plugins_verbose, str);
314 CFRelease(str);
0fae82ee 315 }
5958d7c0
A
316 break;
317 case '?':
318 default :
319 usage(prog);
320 }
321 }
322 argc -= optind;
323 argv += optind;
324
325 /*
326 * display an error if configd is already running and we are
009ee3c6 327 * not solely executing/testing a bundle.
5958d7c0 328 */
009ee3c6
A
329 if (testBundle == NULL) {
330 if (server_active(&service_port)) {
331 exit (EX_UNAVAILABLE);
332 }
5958d7c0
A
333 }
334
5958d7c0
A
335 /* check credentials */
336 if (getuid() != 0) {
0fae82ee
A
337 fprintf(stderr, "%s: permission denied.\n", prog);
338 exit (EX_NOPERM);
5958d7c0
A
339 }
340
009ee3c6
A
341 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
342 /*
343 * if we haven't been asked to run in the foreground
344 * and have not been started by mach_init (i.e. we're
345 * not already running as a Foreground process) then
346 * daemonize ourself.
347 */
5958d7c0
A
348 if (fork_child() == -1) {
349 fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
350 exit (1);
351 }
0fae82ee 352
009ee3c6
A
353 /*
354 * Note: we are now the child process. The parent
355 * waits/exits in fork_child.
356 */
a5f60add
A
357 }
358
009ee3c6
A
359 /*
360 * close file descriptors, establish stdin/stdout/stderr,
361 * setup logging.
362 */
363 if (!forceForeground) {
0fae82ee 364 int logopt = LOG_NDELAY|LOG_PID;
5958d7c0 365
009ee3c6
A
366 init_fds();
367
0fae82ee
A
368 if (_configd_verbose)
369 logopt |= LOG_CONS;
370 openlog("configd", logopt, LOG_DAEMON);
371 } else {
372 _sc_log = FALSE; /* redirect SCLog() to stdout/stderr */
5958d7c0
A
373 }
374
009ee3c6
A
375 /* check/enable trace logging */
376 set_trace();
5958d7c0 377
009ee3c6
A
378 /* record process id */
379 if (testBundle == NULL) {
380 writepid();
381 }
382
383 /* add signal handler to catch a SIGHUP */
5958d7c0
A
384 nact.sa_handler = catcher;
385 sigemptyset(&nact.sa_mask);
386 nact.sa_flags = SA_RESTART;
0fae82ee
A
387 if (sigaction(SIGHUP, &nact, NULL) == -1) {
388 SCLog(_configd_verbose, LOG_ERR,
389 CFSTR("sigaction(SIGHUP, ...) failed: %s"),
390 strerror(errno));
391 }
392
393 /* add signal handler to catch a SIGPIPE */
5958d7c0 394 if (sigaction(SIGPIPE, &nact, NULL) == -1) {
0fae82ee 395 SCLog(_configd_verbose, LOG_ERR,
5958d7c0
A
396 CFSTR("sigaction(SIGPIPE, ...) failed: %s"),
397 strerror(errno));
398 }
399
dbf6a266
A
400 /* add signal handler to catch a SIGTERM */
401 if (sigaction(SIGTERM, &nact, NULL) == -1) {
402 SCLog(_configd_verbose, LOG_ERR,
403 CFSTR("sigaction(SIGTERM, ...) failed: %s"),
404 strerror(errno));
405 }
5958d7c0 406
dbf6a266
A
407 /* create the "shutdown requested" notification port */
408 termRequested = CFMachPortCreate(NULL, term, NULL, NULL);
409
410 // set queue limit
411 limits.mpl_qlimit = 1;
412 status = mach_port_set_attributes(mach_task_self(),
413 CFMachPortGetPort(termRequested),
414 MACH_PORT_LIMITS_INFO,
415 (mach_port_info_t)&limits,
416 MACH_PORT_LIMITS_INFO_COUNT);
417 if (status != KERN_SUCCESS) {
418 perror("mach_port_set_attributes");
419 }
009ee3c6 420
dbf6a266
A
421 // add to our runloop
422 rls = CFMachPortCreateRunLoopSource(NULL, termRequested, 0);
423 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
424 CFRelease(rls);
5958d7c0 425
dbf6a266 426 if (testBundle == NULL) {
0fae82ee 427 /* initialize primary (store management) thread */
009ee3c6
A
428 server_init(service_port, enableRestart);
429
430 if (!forceForeground && (service_port == MACH_PORT_NULL)) {
431 /* synchronize with parent process */
432 kill(getppid(), SIGTERM);
433 }
5958d7c0
A
434
435 /* load/initialize/start bundles into the secondary thread */
436 if (loadBundles) {
009ee3c6 437 objc_setMultithreaded(YES);
5958d7c0 438 plugin_init();
5958d7c0 439 }
5958d7c0 440
0fae82ee 441 /* start primary (store management) thread */
5958d7c0
A
442 server_loop();
443 } else {
444 /* load/initialize/start specified plug-in */
445 plugin_exec((void *)testBundle);
446 }
447
448 exit (EX_OK); // insure the process exit status is 0
449 return 0; // ...and make main fit the ANSI spec.
450}