]> git.saurik.com Git - apple/system_cmds.git/blame - mach_init.tproj/bootstrap.c
system_cmds-258.tar.gz
[apple/system_cmds.git] / mach_init.tproj / bootstrap.c
CommitLineData
1815bff5 1/*
20e66415 2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
1815bff5
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
20e66415
A
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
1815bff5
A
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
20e66415
A
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
1815bff5
A
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * bootstrap -- fundamental service initiator and port server
26 * Mike DeMoney, NeXT, Inc.
27 * Copyright, 1990. All rights reserved.
28 *
29 * bootstrap.c -- implementation of bootstrap main service loop
30 */
31
32/*
33 * Imports
34 */
35#import <mach/mach.h>
36#import <mach/boolean.h>
37#import <mach/message.h>
38#import <mach/notify.h>
39#import <mach/mig_errors.h>
40#include <mach/mach_traps.h>
41#include <mach/mach_interface.h>
42#include <mach/bootstrap.h>
43#include <mach/host_info.h>
44#include <mach/mach_host.h>
45#include <mach/exception.h>
46
47#import <sys/ioctl.h>
b51d5b5f 48#import <sys/types.h>
20e66415
A
49#import <sys/time.h>
50#import <sys/resource.h>
b51d5b5f
A
51#import <sys/wait.h>
52#import <pthread.h>
1815bff5
A
53#import <string.h>
54#import <ctype.h>
55#import <stdio.h>
56#import <libc.h>
20e66415 57#import <paths.h>
1815bff5 58
b51d5b5f 59#import "bootstrap.h"
1815bff5
A
60
61#import "bootstrap_internal.h"
62#import "lists.h"
63#import "error_log.h"
1815bff5
A
64
65/* Mig should produce a declaration for this, but doesn't */
66extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
67
68/*
69 * Exports
70 */
71const char *program_name; /* our name for error messages */
72
b51d5b5f
A
73#ifndef INIT_PATH
74#define INIT_PATH "/sbin/init" /* default init path */
75#endif INIT_PATH
1815bff5 76
b51d5b5f 77uid_t inherited_uid;
1815bff5
A
78mach_port_t inherited_bootstrap_port = MACH_PORT_NULL;
79boolean_t forward_ok = FALSE;
b51d5b5f 80boolean_t shutdown_in_progress = FALSE;
1815bff5
A
81boolean_t debugging = FALSE;
82boolean_t register_self = FALSE;
b51d5b5f
A
83boolean_t force_fork = FALSE;
84const char *register_name;
85task_t bootstrap_self;
1815bff5
A
86
87#ifndef ASSERT
88#define ASSERT(p)
89#endif
90
91/*
92 * Private macros
93 */
94#define NELEM(x) (sizeof(x)/sizeof(x[0]))
95#define END_OF(x) (&(x)[NELEM(x)])
96#define streq(a,b) (strcmp(a,b) == 0)
97
98/*
99 * Private declarations
100 */
1815bff5
A
101static void wait_for_go(mach_port_t init_notify_port);
102static void init_ports(void);
103static void start_server(server_t *serverp);
b51d5b5f 104static void unblock_init(mach_port_t init_notify_port, mach_port_t newBootstrap);
1815bff5
A
105static void exec_server(server_t *serverp);
106static char **argvize(const char *string);
b51d5b5f 107static void *demand_loop(void *arg);
1815bff5 108static void server_loop(void);
b51d5b5f
A
109extern kern_return_t bootstrap_register
110(
111 mach_port_t bootstrap_port,
112 name_t service_name,
113 mach_port_t service_port
114);
1815bff5
A
115
116/*
117 * Private ports we hold receive rights for. We also hold receive rights
118 * for all the privileged ports. Those are maintained in the server
119 * structs.
120 */
121mach_port_t bootstrap_port_set;
b51d5b5f
A
122mach_port_t demand_port_set;
123pthread_t demand_thread;
124
1815bff5 125mach_port_t notify_port;
b51d5b5f 126mach_port_t backup_port;
1815bff5 127
20e66415 128
b51d5b5f 129static void
20e66415
A
130enablecoredumps(boolean_t enabled)
131{
132 struct rlimit rlimit;
133
134 getrlimit(RLIMIT_CORE, &rlimit);
135 rlimit.rlim_cur = (enabled) ? rlimit.rlim_max : 0;
136 setrlimit(RLIMIT_CORE, &rlimit);
137}
138
139static void
140toggle_debug(int signal)
141{
142
143 debugging = (debugging) ? FALSE : TRUE;
144 enablecoredumps(debugging);
145}
146
147static mach_msg_return_t
148inform_server_loop(
149 mach_port_name_t about,
150 mach_msg_option_t options)
b51d5b5f
A
151{
152 mach_port_destroyed_notification_t not;
20e66415 153 mach_msg_size_t size = sizeof(not) - sizeof(not.trailer);
b51d5b5f
A
154
155 not.not_header.msgh_id = DEMAND_REQUEST;
156 not.not_header.msgh_remote_port = backup_port;
157 not.not_header.msgh_local_port = MACH_PORT_NULL;
158 not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
20e66415 159 not.not_header.msgh_size = size;
b51d5b5f
A
160 not.not_body.msgh_descriptor_count = 1;
161 not.not_port.type = MACH_MSG_PORT_DESCRIPTOR;
162 not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME;
163 not.not_port.name = about;
20e66415
A
164 return mach_msg(&not.not_header, MACH_SEND_MSG|options, size,
165 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
b51d5b5f 166}
1815bff5 167
20e66415
A
168static void
169notify_server_loop(mach_port_name_t about)
1815bff5 170{
20e66415 171 mach_msg_return_t result;
1815bff5 172
20e66415
A
173 result = inform_server_loop(about, MACH_MSG_OPTION_NONE);
174 if (result != MACH_MSG_SUCCESS)
175 kern_error(result, "notify_server_loop: mach_msg()");
b51d5b5f
A
176}
177
178void start_shutdown(int signal)
179{
b51d5b5f 180 shutdown_in_progress = TRUE;
20e66415 181 (void) inform_server_loop(MACH_PORT_NULL, MACH_SEND_TIMEOUT);
b51d5b5f
A
182}
183
184int
185main(int argc, char * argv[])
1815bff5
A
186{
187 const char *argp;
188 char c;
1815bff5 189 server_t *serverp;
1815bff5 190 kern_return_t result;
b51d5b5f
A
191 mach_port_t init_notify_port;
192 pthread_attr_t attr;
193 sigset_t mask;
1815bff5 194 int pid;
b51d5b5f
A
195
196 /*
197 * If we are pid one, we have to exec init. Before doing so, we'll
198 * fork a child, and that will become the true mach_init. But we have
199 * to be very careful about ports. They aren't inherited across fork,
200 * so we have to avoid storing port names in memory before the fork that
201 * might not be valid after.
202 */
203 pid = getpid();
204 if (pid == 1)
205 {
206 close(0);
207 freopen("/dev/console", "r", stdin);
208 setbuf(stdin, NULL);
209 close(1);
210 freopen("/dev/console", "w", stdout);
211 setbuf(stdout, NULL);
212 close(2);
213 freopen("/dev/console", "w", stderr);
214 setbuf(stderr, NULL);
215
216 result = mach_port_allocate(
217 mach_task_self(),
218 MACH_PORT_RIGHT_RECEIVE,
219 &init_notify_port);
220 if (result != KERN_SUCCESS)
221 kern_fatal(result, "mach_port_allocate");
222
223 result = mach_port_insert_right(
224 mach_task_self(),
225 init_notify_port,
226 init_notify_port,
227 MACH_MSG_TYPE_MAKE_SEND);
228 if (result != KERN_SUCCESS)
229 kern_fatal(result, "mach_port_insert_right");
230
231 result = task_set_bootstrap_port(
232 mach_task_self(),
233 init_notify_port);
234 if (result != KERN_SUCCESS)
235 kern_fatal(result, "task_set_bootstrap_port");
236
237 pid = fork();
238
239 if (pid < 0)
240 unix_fatal("fork");
241
242 else if (pid != 0) { /* PARENT - will become init when ready */
243 int fd;
244
245 /*
246 * Wait for mach_init ot give us a real bootstrap port
247 */
248 wait_for_go(init_notify_port);
249 info("Execing init");
250
251 close(0);
252 close(1);
253 close(2);
254 fd = open("/dev/tty", O_RDONLY);
255 if (fd >= 0) {
256 ioctl(fd, TIOCNOTTY, 0);
257 close(fd);
258 }
259
260 /* pass our arguments on to init */
261 argv[0] = INIT_PATH;
262 execv(argv[0], argv);
263 exit(1); /* will likely trigger a panic */
264
265 }
20e66415
A
266
267 /*
268 * Child - will continue along as mach_init. Construct
269 * a very basic environment - as much as if we were
270 * actually forked from init (instead of the other way
271 * around):
272 *
273 * Set up the PATH to be approriate for the root user.
274 * Create an initial session.
275 * Establish an initial user.
276 * Disbale core dumps.
277 */
278 setenv("PATH", _PATH_STDPATH, 1);
279 setsid();
280 setlogin("root");
281 enablecoredumps(FALSE);
b51d5b5f
A
282 } else
283 init_notify_port = MACH_PORT_NULL;
1815bff5
A
284
285 /* Initialize error handling */
286 program_name = rindex(*argv, '/');
287 if (program_name)
288 program_name++;
289 else
290 program_name = *argv;
291 argv++; argc--;
292
1815bff5
A
293 /* Parse command line args */
294 while (argc > 0 && **argv == '-') {
1815bff5
A
295 argp = *argv++ + 1; argc--;
296 while (*argp) {
297 switch (c = *argp++) {
298 case 'd':
299 debugging = TRUE;
20e66415 300 enablecoredumps(TRUE);
1815bff5
A
301 break;
302 case 'D':
303 debugging = FALSE;
20e66415 304 enablecoredumps(FALSE);
1815bff5
A
305 break;
306 case 'F':
307 force_fork = TRUE;
308 break;
1815bff5
A
309 case 'r':
310 register_self = forward_ok = TRUE;
311 if (argc > 0) {
312 register_name = *argv++; argc--;
313 } else
314 fatal("-r requires name");
315 break;
b51d5b5f 316 case '-':
1815bff5 317 default:
1815bff5
A
318 break;
319 }
320 }
1815bff5
A
321 }
322
1815bff5 323 /*
b51d5b5f 324 * If we must fork, do it now before we get Mach ports in use
1815bff5 325 */
b51d5b5f 326 if (force_fork) {
1815bff5
A
327 pid = fork();
328 if (pid < 0)
329 unix_fatal("fork");
b51d5b5f
A
330 else if (pid != 0) /* PARENT: just exit */
331 exit(0);
332 }
333
334 /* block all but SIGHUP and SIGTERM and mark us as an init process */
335 setsid();
336 sigfillset(&mask);
337 sigdelset(&mask, SIGHUP);
338 signal(SIGHUP, toggle_debug);
339 sigdelset(&mask, SIGTERM);
340 signal(SIGTERM, start_shutdown);
341 (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL);
342
343 init_errlog(pid == 0); /* are we a daemon? */
344 init_lists();
345
346 /*
347 * This task will become the bootstrap task, so go ahead and
348 * initialize the ports now.
349 */
350 bootstrap_self = mach_task_self();
351 inherited_uid = getuid();
352 init_ports();
1815bff5 353
b51d5b5f
A
354 log("Started with uid=%d%s%s%s",
355 inherited_uid,
356 (register_self) ? " registered-as=" : "",
357 (register_self) ? register_name : "",
358 (debugging) ? " in debug-mode" : "");
1815bff5 359
1815bff5 360
b51d5b5f
A
361 /*
362 * If we are supposed to coordinate with init, we have to
363 * get that port again, because we only have a (probably wrong)
364 * name in memory, not a proper right.
365 */
366 if (init_notify_port != MACH_PORT_NULL) {
367 result = task_get_bootstrap_port(
368 bootstrap_self,
369 &init_notify_port);
370 if (result != KERN_SUCCESS)
371 kern_fatal(result, "task_get_bootstrap_port");
372
373 unblock_init(init_notify_port, bootstraps.bootstrap_port);
374
375 result = task_set_bootstrap_port(
376 bootstrap_self,
377 MACH_PORT_NULL);
378 if (result != KERN_SUCCESS)
379 kern_fatal(result, "task_set_bootstrap_port");
380
381 result = mach_port_deallocate(
382 bootstrap_self,
383 init_notify_port);
384 if (result != KERN_SUCCESS)
385 kern_fatal(result, "mach_port_deallocate");
386
387 forward_ok = FALSE;
388 inherited_bootstrap_port = MACH_PORT_NULL;
389
390 } else {
391
392 /* get inherited bootstrap port */
393 result = task_get_bootstrap_port(
394 bootstrap_self,
395 &inherited_bootstrap_port);
396 if (result != KERN_SUCCESS)
397 kern_fatal(result, "task_get_bootstrap_port");
398
399 /* We set this explicitly as we start each child */
400 task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL);
401 if (inherited_bootstrap_port == MACH_PORT_NULL)
402 forward_ok = FALSE;
403
404 /* register "self" port with anscestor */
405 if (register_self && forward_ok) {
406 result = bootstrap_register(
407 inherited_bootstrap_port,
408 (char *)register_name,
409 bootstraps.bootstrap_port);
1815bff5 410 if (result != KERN_SUCCESS)
b51d5b5f 411 kern_fatal(result, "register self");
1815bff5 412 }
1815bff5 413 }
1815bff5 414
b51d5b5f
A
415
416 /* Kick off all continuously running server processes */
417 for ( serverp = FIRST(servers)
418 ; !IS_END(serverp, servers)
419 ; serverp = NEXT(serverp))
420 if (serverp->servertype != DEMAND)
421 start_server(serverp);
422
423 pthread_attr_init (&attr);
424 pthread_attr_setdetachstate ( &attr, PTHREAD_CREATE_DETACHED );
425 result = pthread_create(
426 &demand_thread,
427 &attr,
428 demand_loop,
429 NULL);
430 if (result) {
431 unix_error("pthread_create()");
1815bff5
A
432 exit(1);
433 }
1815bff5 434
b51d5b5f
A
435 /* Process bootstrap service requests */
436 server_loop(); /* Should never return */
437 exit(1);
1815bff5
A
438}
439
440static void
441wait_for_go(mach_port_t init_notify_port)
442{
b51d5b5f 443 struct {
1815bff5
A
444 mach_msg_header_t hdr;
445 mach_msg_trailer_t trailer;
b51d5b5f 446 } init_go_msg;
1815bff5
A
447 kern_return_t result;
448
b51d5b5f 449 /*
1815bff5
A
450 * For now, we just blindly wait until we receive a message or
451 * timeout. We don't expect any notifications, and if we get one,
452 * it probably means something dire has happened; so we might as
453 * well give a shot at letting init run.
454 */
b51d5b5f
A
455 result = mach_msg(
456 &init_go_msg.hdr, MACH_RCV_MSG,
457 0, sizeof(init_go_msg), init_notify_port,
458 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
459 if (result != KERN_SUCCESS) {
460 kern_error(result, "mach_msg(receive) failed in wait_for_go");
461 }
462 result = task_set_bootstrap_port(
463 mach_task_self(),
464 init_go_msg.hdr.msgh_remote_port);
465 if (result != KERN_SUCCESS) {
466 kern_error(result, "task_get_bootstrap_port()");
467 }
1815bff5
A
468}
469
b51d5b5f
A
470
471static void
472unblock_init(mach_port_t init_notify_port,
473 mach_port_t newBootstrap)
474{
475 mach_msg_header_t init_go_msg;
476 kern_return_t result;
477
478 /*
479 * Proc 1 is blocked in a msg_receive on its notify port, this lets
480 * it continue, and we hand off its new bootstrap port
481 */
482 init_go_msg.msgh_remote_port = init_notify_port;
483 init_go_msg.msgh_local_port = newBootstrap;
484 init_go_msg.msgh_bits = MACH_MSGH_BITS(
485 MACH_MSG_TYPE_COPY_SEND,
486 MACH_MSG_TYPE_MAKE_SEND);
487 init_go_msg.msgh_size = sizeof(init_go_msg);
488 result = mach_msg_send(&init_go_msg);
489 if (result != KERN_SUCCESS)
490 kern_fatal(result, "unblock_init mach_msg_send() failed");
491 debug("sent go message");
492}
493
494
1815bff5
A
495static void
496init_ports(void)
497{
498 kern_return_t result;
499 service_t *servicep;
1815bff5
A
500
501 /*
502 * This task will become the bootstrap task.
503 */
b51d5b5f
A
504 /* Create port set that server loop listens to */
505 result = mach_port_allocate(
506 bootstrap_self,
507 MACH_PORT_RIGHT_PORT_SET,
508 &bootstrap_port_set);
1815bff5 509 if (result != KERN_SUCCESS)
b51d5b5f 510 kern_fatal(result, "port_set_allocate");
1815bff5 511
b51d5b5f
A
512 /* Create demand port set that second thread listens to */
513 result = mach_port_allocate(
514 bootstrap_self,
515 MACH_PORT_RIGHT_PORT_SET,
516 &demand_port_set);
1815bff5
A
517 if (result != KERN_SUCCESS)
518 kern_fatal(result, "port_set_allocate");
b51d5b5f 519
1815bff5 520 /* Create notify port and add to server port set */
b51d5b5f
A
521 result = mach_port_allocate(
522 bootstrap_self,
523 MACH_PORT_RIGHT_RECEIVE,
524 &notify_port);
1815bff5
A
525 if (result != KERN_SUCCESS)
526 kern_fatal(result, "mach_port_allocate");
527
b51d5b5f 528 result = mach_port_move_member(
1815bff5 529 bootstrap_self,
1815bff5 530 notify_port,
b51d5b5f
A
531 bootstrap_port_set);
532 if (result != KERN_SUCCESS)
533 kern_fatal(result, "mach_port_move_member");
534
535 /* Create backup port and add to server port set */
536 result = mach_port_allocate(
537 bootstrap_self,
538 MACH_PORT_RIGHT_RECEIVE,
539 &backup_port);
1815bff5 540 if (result != KERN_SUCCESS)
b51d5b5f 541 kern_fatal(result, "mach_port_allocate");
1815bff5 542
b51d5b5f
A
543 result = mach_port_move_member(
544 bootstrap_self,
545 backup_port,
546 bootstrap_port_set);
1815bff5
A
547 if (result != KERN_SUCCESS)
548 kern_fatal(result, "mach_port_move_member");
549
550 /* Create "self" port and add to server port set */
b51d5b5f
A
551 result = mach_port_allocate(
552 bootstrap_self,
553 MACH_PORT_RIGHT_RECEIVE,
554 &bootstraps.bootstrap_port);
1815bff5
A
555 if (result != KERN_SUCCESS)
556 kern_fatal(result, "mach_port_allocate");
b51d5b5f
A
557 result = mach_port_insert_right(
558 bootstrap_self,
559 bootstraps.bootstrap_port,
560 bootstraps.bootstrap_port,
561 MACH_MSG_TYPE_MAKE_SEND);
562 if (result != KERN_SUCCESS)
563 kern_fatal(result, "mach_port_insert_right");
564
565 /* keep the root bootstrap port "active" */
566 bootstraps.requestor_port = bootstraps.bootstrap_port;
567
568 result = mach_port_move_member(
569 bootstrap_self,
570 bootstraps.bootstrap_port,
571 bootstrap_port_set);
1815bff5
A
572 if (result != KERN_SUCCESS)
573 kern_fatal(result, "mach_port_move_member");
b51d5b5f 574
1815bff5
A
575 /*
576 * Allocate service ports for declared services.
577 */
578 for ( servicep = FIRST(services)
579 ; ! IS_END(servicep, services)
580 ; servicep = NEXT(servicep))
581 {
582 switch (servicep->servicetype) {
583 case DECLARED:
b51d5b5f
A
584 result = mach_port_allocate(
585 bootstrap_self,
586 MACH_PORT_RIGHT_RECEIVE,
587 &(servicep->port));
1815bff5
A
588 if (result != KERN_SUCCESS)
589 kern_fatal(result, "mach_port_allocate");
b51d5b5f
A
590
591 result = mach_port_insert_right(
592 bootstrap_self,
593 servicep->port,
594 servicep->port,
595 MACH_MSG_TYPE_MAKE_SEND);
1815bff5
A
596 if (result != KERN_SUCCESS)
597 kern_fatal(result, "mach_port_insert_right");
b51d5b5f 598 info("Declared port %x for service %s",
1815bff5
A
599 servicep->port,
600 servicep->name);
b51d5b5f
A
601
602 if (servicep->server != NULL_SERVER &&
603 servicep->server->servertype == DEMAND) {
604 result = mach_port_move_member(
605 bootstrap_self,
606 servicep->port,
607 demand_port_set);
608 if (result != KERN_SUCCESS)
609 kern_fatal(result, "mach_port_move_member");
610 }
1815bff5 611 break;
b51d5b5f 612
1815bff5
A
613 case SELF:
614 servicep->port = bootstraps.bootstrap_port;
b51d5b5f
A
615 servicep->server = new_server(&bootstraps,
616 program_name,
617 inherited_uid,
618 MACHINIT);
619 info("Set port %x for self port",
1815bff5
A
620 bootstraps.bootstrap_port);
621 break;
b51d5b5f 622
1815bff5
A
623 case REGISTERED:
624 fatal("Can't allocate REGISTERED port!?!");
625 break;
626 }
627 }
628}
629
b51d5b5f
A
630boolean_t
631active_bootstrap(bootstrap_info_t *bootstrap)
632{
633 return (bootstrap->requestor_port != MACH_PORT_NULL);
634}
635
636boolean_t
637useless_server(server_t *serverp)
638{
639 return ( !active_bootstrap(serverp->bootstrap) ||
640 !lookup_service_by_server(serverp) ||
641 !serverp->activity);
642}
643
644boolean_t
645active_server(server_t *serverp)
646{
20e66415 647 return ( serverp->port ||
b51d5b5f
A
648 serverp->task_port || serverp->active_services);
649}
650
1815bff5 651static void
b51d5b5f
A
652reap_server(server_t *serverp)
653{
654 kern_return_t result;
655 pid_t presult;
656 int wstatus;
657
658 /*
659 * Reap our children.
660 */
661 presult = waitpid(serverp->pid, &wstatus, WNOHANG);
662 if (presult != serverp->pid) {
663 unix_error("waitpid: cmd = %s", serverp->cmd);
664 } else if (wstatus) {
665 log("Server %x in bootstrap %x uid %d: \"%s\": %s %d [pid %d]",
666 serverp->port, serverp->bootstrap->bootstrap_port,
667 serverp->uid, serverp->cmd,
668 ((WIFEXITED(wstatus)) ?
669 "exited with non-zero status" :
670 "exited as a result of signal"),
671 ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : WTERMSIG(wstatus)),
672 serverp->pid);
673 }
674 serverp->pid = 0;
675
676 /*
677 * Release the server task port reference, if we ever
678 * got it in the first place.
679 */
680 if (serverp->task_port != MACH_PORT_NULL) {
681 result = mach_port_deallocate(
682 mach_task_self(),
683 serverp->task_port);
684 if (result != KERN_SUCCESS)
685 kern_error(result, "mach_port_deallocate");
686 serverp->task_port = MACH_PORT_NULL;
687 }
688}
689
690static void
691demand_server(server_t *serverp)
692{
693 service_t *servicep;
694 kern_return_t result;
695
696 /*
697 * For on-demand servers, make sure that the service ports are
698 * back in on-demand portset. Active service ports should come
699 * back through a PORT_DESTROYED notification. We only have to
700 * worry about the inactive ports that may have been previously
701 * pulled from the set but never checked-in by the server.
702 */
703
704 for ( servicep = FIRST(services)
705 ; !IS_END(servicep, services)
706 ; servicep = NEXT(servicep))
707 {
708 if (serverp == servicep->server && !servicep->isActive) {
709 result = mach_port_move_member(
710 mach_task_self(),
711 servicep->port,
712 demand_port_set);
713 if (result != KERN_SUCCESS)
714 kern_fatal(result, "mach_port_move_member");
715 }
716 }
717}
718
719static
720void dispatch_server(server_t *serverp)
721{
722 if (!active_server(serverp)) {
723 if (useless_server(serverp))
724 delete_server(serverp);
725 else if (serverp->servertype == RESTARTABLE)
726 start_server(serverp);
727 else if (serverp->servertype == DEMAND)
728 demand_server(serverp);
729 }
730}
731
732void
733setup_server(server_t *serverp)
1815bff5
A
734{
735 kern_return_t result;
736 mach_port_t old_port;
1815bff5 737
1815bff5 738 /* Allocate privileged port for requests from service */
b51d5b5f
A
739 result = mach_port_allocate(mach_task_self(),
740 MACH_PORT_RIGHT_RECEIVE ,
741 &serverp->port);
742 info("Allocating port %x for server %s", serverp->port, serverp->cmd);
1815bff5
A
743 if (result != KERN_SUCCESS)
744 kern_fatal(result, "port_allocate");
745
b51d5b5f
A
746 /* Request no-senders notification so we can tell when server dies */
747 result = mach_port_request_notification(mach_task_self(),
748 serverp->port,
749 MACH_NOTIFY_NO_SENDERS,
750 1,
751 serverp->port,
752 MACH_MSG_TYPE_MAKE_SEND_ONCE,
753 &old_port);
754 if (result != KERN_SUCCESS)
755 kern_fatal(result, "mach_port_request_notification");
756
1815bff5
A
757 /* Add privileged server port to bootstrap port set */
758 result = mach_port_move_member(mach_task_self(),
b51d5b5f
A
759 serverp->port,
760 bootstrap_port_set);
1815bff5
A
761 if (result != KERN_SUCCESS)
762 kern_fatal(result, "mach_port_move_member");
b51d5b5f
A
763}
764
765static void
766start_server(server_t *serverp)
767{
768 kern_return_t result;
769 mach_port_t old_port;
770 int pid;
1815bff5
A
771
772 /*
773 * Do what's appropriate to get bootstrap port setup in server task
774 */
775 switch (serverp->servertype) {
1815bff5
A
776
777 case MACHINIT:
778 break;
779
780 case SERVER:
b51d5b5f 781 case DEMAND:
1815bff5 782 case RESTARTABLE:
b51d5b5f 783 if (!serverp->port)
b51d5b5f
A
784 setup_server(serverp);
785
786 serverp->activity = 0;
787
788 /* Insert a send right */
789 result = mach_port_insert_right(mach_task_self(),
790 serverp->port,
791 serverp->port,
792 MACH_MSG_TYPE_MAKE_SEND);
793 if (result != KERN_SUCCESS)
794 kern_fatal(result, "mach_port_insert_right");
795
1815bff5 796 /* Give trusted service a unique bootstrap port */
b51d5b5f
A
797 result = task_set_bootstrap_port(mach_task_self(),
798 serverp->port);
1815bff5
A
799 if (result != KERN_SUCCESS)
800 kern_fatal(result, "task_set_bootstrap_port");
801
b51d5b5f
A
802 result = mach_port_deallocate(mach_task_self(),
803 serverp->port);
804 if (result != KERN_SUCCESS)
805 kern_fatal(result, "mach_port_deallocate");
806
1815bff5 807 pid = fork();
b51d5b5f 808 if (pid < 0) {
1815bff5 809 unix_error("fork");
b51d5b5f 810 } else if (pid == 0) { /* CHILD */
1815bff5
A
811 exec_server(serverp);
812 exit(1);
813 } else { /* PARENT */
b51d5b5f
A
814
815 result = task_set_bootstrap_port(
816 mach_task_self(),
817 MACH_PORT_NULL);
1815bff5
A
818 if (result != KERN_SUCCESS)
819 kern_fatal(result, "task_set_bootstrap_port");
820
b51d5b5f
A
821 info("Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]",
822 serverp->port, serverp->bootstrap->bootstrap_port,
823 serverp->uid, serverp->cmd, pid);
824 serverp->pid = pid;
825 result = task_for_pid(
826 mach_task_self(),
827 pid,
828 &serverp->task_port);
829 if (result != KERN_SUCCESS) {
830 kern_error(result, "getting server task port");
831 reap_server(serverp);
832 dispatch_server(serverp);
833 break;
834 }
835
836 /* Request dead name notification to tell when task dies */
837 result = mach_port_request_notification(
838 mach_task_self(),
839 serverp->task_port,
840 MACH_NOTIFY_DEAD_NAME,
841 0,
842 notify_port,
843 MACH_MSG_TYPE_MAKE_SEND_ONCE,
844 &old_port);
845 if (result != KERN_SUCCESS) {
846 kern_error(result, "mach_port_request_notification");
847 reap_server(serverp);
848 dispatch_server(serverp);
849 }
1815bff5
A
850 }
851 break;
852 }
853}
854
1815bff5
A
855static void
856exec_server(server_t *serverp)
857{
b51d5b5f
A
858 char **argv;
859 sigset_t mask;
860
1815bff5
A
861 /*
862 * Setup environment for server, someday this should be Mach stuff
863 * rather than Unix crud
864 */
1815bff5 865 argv = argvize(serverp->cmd);
1815bff5
A
866 close_errlog();
867
b51d5b5f
A
868 if (serverp->uid != inherited_uid)
869 if (setuid(serverp->uid) < 0)
870 unix_fatal("Disabled server %x bootstrap %x: \"%s\": setuid(%d)",
871 serverp->port, serverp->bootstrap->bootstrap_port,
872 serverp->cmd, serverp->uid);
873
874 if (setsid() < 0) {
875 /*
876 * We can't keep this from happening, but we shouldn't start
877 * the server not as a process group leader. So, just fake like
878 * there was real activity, and exit the child. If needed,
879 * we'll re-launch it under another pid.
880 */
881 serverp->activity = 1;
882 unix_fatal("Temporary failure server %x bootstrap %x: \"%s\": setsid()",
883 serverp->port, serverp->bootstrap->bootstrap_port,
884 serverp->cmd);
1815bff5
A
885 }
886
b51d5b5f
A
887 sigemptyset(&mask);
888 (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL);
1815bff5 889
b51d5b5f
A
890 execv(argv[0], argv);
891 unix_fatal("Disabled server %x bootstrap %x: \"%s\": exec()",
892 serverp->port,
893 serverp->bootstrap->bootstrap_port,
894 serverp->cmd);
1815bff5
A
895}
896
897static char **
898argvize(const char *string)
899{
900 static char *argv[100], args[1000];
901 const char *cp;
902 char *argp, term;
903 int nargs;
904
905 /*
906 * Convert a command line into an argv for execv
907 */
908 nargs = 0;
909 argp = args;
910
911 for (cp = string; *cp;) {
912 while (isspace(*cp))
913 cp++;
914 term = (*cp == '"') ? *cp++ : '\0';
915 if (nargs < NELEM(argv))
916 argv[nargs++] = argp;
917 while (*cp && (term ? *cp != term : !isspace(*cp))
918 && argp < END_OF(args)) {
919 if (*cp == '\\')
920 cp++;
921 *argp++ = *cp;
922 if (*cp)
923 cp++;
924 }
925 *argp++ = '\0';
926 }
927 argv[nargs] = NULL;
928 return argv;
929}
930
b51d5b5f
A
931static void *
932demand_loop(void *arg)
933{
934 mach_msg_empty_rcv_t dummy;
935 kern_return_t dresult;
936
937
938 for(;;) {
939 mach_port_name_array_t members;
940 mach_msg_type_number_t membersCnt;
941 mach_port_status_t status;
942 mach_msg_type_number_t statusCnt;
943 int i;
944
945 /*
946 * Receive indication of message on demand service
947 * ports without actually receiving the message (we'll
948 * let the actual server do that.
949 */
950 dresult = mach_msg(
951 &dummy.header,
952 MACH_RCV_MSG|MACH_RCV_LARGE,
953 0,
954 0,
955 demand_port_set,
956 0,
957 MACH_PORT_NULL);
958 if (dresult != MACH_RCV_TOO_LARGE) {
959 kern_error(dresult, "demand_loop: mach_msg()");
960 continue;
961 }
962
963 /*
964 * If we are shutting down, there is no use processing
965 * any more of these messages.
966 */
967 if (shutdown_in_progress == TRUE)
968 return arg;
969
970 /*
971 * Some port(s) now have messages on them, find out
972 * which ones (there is no indication of which port
973 * triggered in the MACH_RCV_TOO_LARGE indication).
974 */
975 dresult = mach_port_get_set_status(
976 mach_task_self(),
977 demand_port_set,
978 &members,
979 &membersCnt);
980 if (dresult != KERN_SUCCESS) {
981 kern_error(dresult, "demand_loop: mach_port_get_set_status()");
982 continue;
983 }
984
985 for (i = 0; i < membersCnt; i++) {
986 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
987 dresult = mach_port_get_attributes(
988 mach_task_self(),
989 members[i],
990 MACH_PORT_RECEIVE_STATUS,
991 (mach_port_info_t)&status,
992 &statusCnt);
993 if (dresult != KERN_SUCCESS) {
994 kern_error(dresult, "demand_loop: mach_port_get_attributes()");
995 continue;
996 }
997
998 /*
999 * For each port with messages, take it out of the
1000 * demand service portset, and inform the main thread
1001 * that it might have to start the server responsible
1002 * for it.
1003 */
1004 if (status.mps_msgcount) {
1005 dresult = mach_port_move_member(
1006 mach_task_self(),
1007 members[i],
1008 MACH_PORT_NULL);
1009 if (dresult != KERN_SUCCESS) {
1010 kern_error(dresult, "demand_loop: mach_port_move_member()");
1011 continue;
1012 }
1013 notify_server_loop(members[i]);
1014 }
1015 }
1016
1017 dresult = vm_deallocate(
1018 mach_task_self(),
1019 (vm_address_t) members,
1020 (vm_size_t) membersCnt * sizeof(mach_port_name_t));
1021 if (dresult != KERN_SUCCESS) {
1022 kern_error(dresult, "demand_loop: vm_deallocate()");
1023 continue;
1024 }
1025 }
1026 return NULL;
1027}
1028
1815bff5 1029/*
b51d5b5f 1030 * server_demux -- processes requests off our service port
1815bff5
A
1031 * Also handles notifications
1032 */
1815bff5 1033
b51d5b5f
A
1034static boolean_t
1035server_demux(
1036 mach_msg_header_t *Request,
1037 mach_msg_header_t *Reply)
1815bff5
A
1038{
1039 bootstrap_info_t *bootstrap;
1040 service_t *servicep;
1041 server_t *serverp;
1042 kern_return_t result;
b51d5b5f 1043 mig_reply_error_t *reply;
1815bff5 1044
b51d5b5f 1045 debug("received message on port %x\n", Request->msgh_local_port);
1815bff5 1046
b51d5b5f
A
1047 /*
1048 * Do minimal cleanup and then exit.
1049 */
1050 if (shutdown_in_progress == TRUE) {
1051 log("Shutting down. Deactivating root bootstrap (%x) ...",
1052 bootstraps.bootstrap_port);
1053 deactivate_bootstrap(&bootstraps);
1054 log("Done.");
1055 exit(0);
1056 }
1057
1058 reply = (mig_reply_error_t *)Reply;
1815bff5
A
1059
1060 /*
1061 * Pick off notification messages
1062 */
b51d5b5f
A
1063 if (Request->msgh_local_port == notify_port) {
1064 mach_port_name_t np;
1065
1066 memset(reply, 0, sizeof(*reply));
1067 switch (Request->msgh_id) {
1068 case MACH_NOTIFY_DEAD_NAME:
1069 np = ((mach_dead_name_notification_t *)Request)->not_port;
1070 debug("Notified dead name %x", np);
1071
1072 if (np == inherited_bootstrap_port) {
1073 inherited_bootstrap_port = MACH_PORT_NULL;
1074 forward_ok = FALSE;
1075 }
1815bff5 1076
b51d5b5f
A
1077 /*
1078 * Check to see if a subset requestor port was deleted.
1079 */
1080 while (bootstrap = lookup_bootstrap_by_req_port(np)) {
1081 debug("Received dead name notification for bootstrap subset %x requestor port %x",
1082 bootstrap->bootstrap_port, bootstrap->requestor_port);
1083 mach_port_deallocate(
1084 mach_task_self(),
1085 bootstrap->requestor_port);
1086 bootstrap->requestor_port = MACH_PORT_NULL;
1087 deactivate_bootstrap(bootstrap);
1088 }
1815bff5 1089
b51d5b5f
A
1090 /*
1091 * Check to see if a defined service has gone
1092 * away.
1093 */
1094 while (servicep = lookup_service_by_port(np)) {
1095 /*
1096 * Port gone, registered service died.
1097 */
1098 debug("Received dead name notification for service %s "
1099 "on bootstrap port %x\n",
1100 servicep->name, servicep->bootstrap);
1101 debug("Service %s failed - deallocate", servicep->name);
1102 delete_service(servicep);
1103 }
1815bff5 1104
1815bff5 1105 /*
b51d5b5f
A
1106 * Check to see if a launched server task has gone
1107 * away.
1815bff5 1108 */
b51d5b5f
A
1109 if (serverp = lookup_server_by_task_port(np)) {
1110 /*
1111 * Port gone, server died.
1112 */
1113 debug("Received task death notification for server %s ",
1114 serverp->cmd);
1115 reap_server(serverp);
1116 dispatch_server(serverp);
1117 }
1118
1119 mach_port_deallocate(mach_task_self(), np);
1120 reply->RetCode = KERN_SUCCESS;
1121 break;
1122
1123 case MACH_NOTIFY_PORT_DELETED:
1124 np = ((mach_port_deleted_notification_t *)Request)->not_port;
1125 debug("port deleted notification on 0x%x\n", np);
1126 reply->RetCode = KERN_SUCCESS;
1127 break;
1128
1129 case MACH_NOTIFY_SEND_ONCE:
1130 debug("notification send-once right went unused\n");
1131 reply->RetCode = KERN_SUCCESS;
1132 break;
1133
1134 default:
1135 error("Unexpected notification: %d", Request->msgh_id);
1136 reply->RetCode = KERN_FAILURE;
1137 break;
1815bff5 1138 }
1815bff5 1139 }
1815bff5 1140
b51d5b5f
A
1141 else if (Request->msgh_local_port == backup_port) {
1142 mach_port_name_t np;
1143
1144 memset(reply, 0, sizeof(*reply));
1145
1146 np = ((mach_port_destroyed_notification_t *)Request)->not_port.name;
1147 servicep = lookup_service_by_port(np);
1148 if (servicep != NULL) {
1149 server_t *serverp = servicep->server;
1150
1151 switch (Request->msgh_id) {
1152
1153 case MACH_NOTIFY_PORT_DESTROYED:
1154 /*
1155 * Port sent back to us, server died.
1156 */
1157 debug("Received destroyed notification for service %s",
1158 servicep->name);
1159 debug("Service %x bootstrap %x backed up: %s",
1160 servicep->port, servicep->bootstrap->bootstrap_port,
1161 servicep->name);
1162 ASSERT(canReceive(servicep->port));
1163 servicep->isActive = FALSE;
1164 serverp->active_services--;
1165 dispatch_server(serverp);
1166 reply->RetCode = KERN_SUCCESS;
1167 break;
1815bff5 1168
b51d5b5f
A
1169 case DEMAND_REQUEST:
1170 /* message reflected over from demand start thread */
1171 if (!active_server(serverp))
1172 start_server(serverp);
1173 reply->RetCode = KERN_SUCCESS;
1174 break;
1175
1176 default:
1177 debug("Mysterious backup_port notification %d", Request->msgh_id);
1178 reply->RetCode = KERN_FAILURE;
1179 break;
1180 }
1815bff5 1181 } else {
b51d5b5f
A
1182 debug("Backup_port notification - previously deleted service");
1183 reply->RetCode = KERN_FAILURE;
1815bff5 1184 }
b51d5b5f
A
1185 }
1186
1187 else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
1188 mach_port_t ns = Request->msgh_local_port;
1189
1190 if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) {
1191 /*
1192 * A server we launched has released his bootstrap
1193 * port send right. We won't re-launch him unless
1194 * his services came back to roost. But we need to
1195 * destroy the bootstrap port for fear of leaking.
1196 */
1197 debug("server %s dropped server port", serverp->cmd);
1198 serverp->port = MACH_PORT_NULL;
1199 dispatch_server(serverp);
1200 } else if (bootstrap = lookup_bootstrap_by_port(ns)) {
1201 /*
1202 * The last direct user of a deactivated bootstrap went away.
1203 * We can finally free it.
1204 */
1205 debug("Deallocating bootstrap %x: no more clients", ns);
1206 bootstrap->bootstrap_port = MACH_PORT_NULL;
1207 deallocate_bootstrap(bootstrap);
1815bff5 1208 }
b51d5b5f
A
1209
1210 result = mach_port_mod_refs(
1211 mach_task_self(),
1212 ns,
1213 MACH_PORT_RIGHT_RECEIVE,
1214 -1);
1215 if (result != KERN_SUCCESS)
1216 kern_fatal(result, "mach_port_mod_refs");
1217
1218 memset(reply, 0, sizeof(*reply));
1219 reply->RetCode = KERN_SUCCESS;
1220 }
1221
1222 else { /* must be a service request */
1223 debug("Handled request.");
1224 return bootstrap_server(Request, Reply);
1815bff5 1225 }
b51d5b5f 1226 return TRUE;
1815bff5
A
1227}
1228
1229/*
b51d5b5f
A
1230 * server_loop -- pick requests off our service port and process them
1231 * Also handles notifications
1815bff5 1232 */
b51d5b5f
A
1233#define bootstrapMaxRequestSize 1024
1234#define bootstrapMaxReplySize 1024
1235
1236static void
1237server_loop(void)
1815bff5 1238{
b51d5b5f
A
1239 mach_msg_return_t mresult;
1240
1241 for (;;) {
1242 mresult = mach_msg_server(
1243 server_demux,
1244 bootstrapMaxRequestSize,
1245 bootstrap_port_set,
1246 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)|
1247 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
1248 if (mresult != MACH_MSG_SUCCESS)
1249 kern_error(mresult, "mach_msg_server");
1250 }
1815bff5
A
1251}
1252
1253boolean_t
1254canReceive(mach_port_t port)
1255{
1256 mach_port_type_t p_type;
1257 kern_return_t result;
1258
1259 result = mach_port_type(mach_task_self(), port, &p_type);
1260 if (result != KERN_SUCCESS) {
1261 kern_error(result, "port_type");
1262 return FALSE;
1263 }
1264 return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0);
1265}
1266
1267
1268boolean_t
1269canSend(mach_port_t port)
1270{
1271 mach_port_type_t p_type;
1272 kern_return_t result;
1273
1274 result = mach_port_type(mach_task_self(), port, &p_type);
1275 if (result != KERN_SUCCESS) {
1276 kern_error(result, "port_type");
1277 return FALSE;
1278 }
1279 return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0);
1280}