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