]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/bootstrap.c
e309a31bf08973926a7e7745c9b1be207ec17761
[apple/launchd.git] / launchd / src / bootstrap.c
1 /*
2 * Copyright (c) 1999-2004 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 #include <mach/mach.h>
36 #include <mach/mach_error.h>
37 #include <mach/boolean.h>
38 #include <mach/message.h>
39 #include <mach/notify.h>
40 #include <mach/mig_errors.h>
41 #include <mach/mach_traps.h>
42 #include <mach/mach_interface.h>
43 #include <mach/bootstrap.h>
44 #include <mach/host_info.h>
45 #include <mach/mach_host.h>
46 #include <mach/exception.h>
47
48 #include <sys/ioctl.h>
49 #include <sys/types.h>
50 #include <sys/time.h>
51 #include <sys/resource.h>
52 #include <sys/wait.h>
53 #include <pthread.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <stdbool.h>
59 #include <libc.h>
60 #include <paths.h>
61 #include <syslog.h>
62 #include <pwd.h>
63
64 #include <bsm/audit.h>
65 #include <bsm/libbsm.h>
66
67 #include "bootstrap.h"
68 #include "bootstrap_internal.h"
69 #include "lists.h"
70 #include "launchd.h"
71
72 /* Mig should produce a declaration for this, but doesn't */
73 extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
74
75 auditinfo_t inherited_audit;
76 mach_port_t inherited_bootstrap_port = MACH_PORT_NULL;
77 bool forward_ok = false;
78 bool debugging = false;
79 bool register_self = false;
80 const char *register_name = NULL;
81 task_t bootstrap_self = MACH_PORT_NULL;
82
83 static uid_t inherited_uid = 0;
84 static bool shutdown_in_progress = false;
85
86 #ifndef ASSERT
87 #define ASSERT(p)
88 #endif
89
90 /*
91 * Private macros
92 */
93 #define NELEM(x) (sizeof(x)/sizeof(x[0]))
94 #define END_OF(x) (&(x)[NELEM(x)])
95 #define streq(a,b) (strcmp(a,b) == 0)
96
97 /*
98 * Private declarations
99 */
100 static void init_ports(void);
101 static void start_server(server_t *serverp);
102 static void exec_server(server_t *serverp);
103 static char **argvize(const char *string);
104 static void *demand_loop(void *arg);
105 void *mach_server_loop(void *);
106 extern kern_return_t bootstrap_register
107 (
108 mach_port_t bootstrapport,
109 name_t servicename,
110 mach_port_t serviceport
111 );
112
113 /*
114 * Private ports we hold receive rights for. We also hold receive rights
115 * for all the privileged ports. Those are maintained in the server
116 * structs.
117 */
118 mach_port_t bootstrap_port_set;
119 mach_port_t demand_port_set;
120 pthread_t demand_thread;
121
122 mach_port_t notify_port;
123 mach_port_t backup_port;
124
125
126 static mach_msg_return_t
127 inform_server_loop(
128 mach_port_name_t about,
129 mach_msg_option_t options)
130 {
131 mach_port_destroyed_notification_t not;
132 mach_msg_size_t size = sizeof(not) - sizeof(not.trailer);
133
134 not.not_header.msgh_id = DEMAND_REQUEST;
135 not.not_header.msgh_remote_port = backup_port;
136 not.not_header.msgh_local_port = MACH_PORT_NULL;
137 not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
138 not.not_header.msgh_size = size;
139 not.not_body.msgh_descriptor_count = 1;
140 not.not_port.type = MACH_MSG_PORT_DESCRIPTOR;
141 not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME;
142 not.not_port.name = about;
143 return mach_msg(&not.not_header, MACH_SEND_MSG|options, size,
144 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
145 }
146
147 static void
148 notify_server_loop(mach_port_name_t about)
149 {
150 mach_msg_return_t result;
151
152 result = inform_server_loop(about, MACH_MSG_OPTION_NONE);
153 if (result != MACH_MSG_SUCCESS)
154 syslog(LOG_ERR, "notify_server_loop: mach_msg(): %s", mach_error_string(result));
155 }
156
157 void mach_start_shutdown(__unused int signalnum)
158 {
159 shutdown_in_progress = TRUE;
160 (void) inform_server_loop(MACH_PORT_NULL, MACH_SEND_TIMEOUT);
161 }
162
163 mach_port_t mach_init_init(void)
164 {
165 kern_return_t result;
166 pthread_attr_t attr;
167
168 bootstrap_self = mach_task_self();
169 inherited_uid = getuid();
170 getaudit(&inherited_audit);
171 init_lists();
172 init_ports();
173
174 result = task_get_bootstrap_port(bootstrap_self, &inherited_bootstrap_port);
175 if (result != KERN_SUCCESS) {
176 syslog(LOG_ALERT, "task_get_bootstrap_port(): %s", mach_error_string(result));
177 exit(EXIT_FAILURE);
178 }
179 if (inherited_bootstrap_port == MACH_PORT_NULL)
180 forward_ok = FALSE;
181
182 /* We set this explicitly as we start each child */
183 task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL);
184
185 /* register "self" port with anscestor */
186 if (register_self && forward_ok) {
187 result = bootstrap_register(inherited_bootstrap_port,
188 (char *)register_name,
189 bootstraps.bootstrap_port);
190 if (result != KERN_SUCCESS)
191 panic("register self(): %s", mach_error_string(result));
192 }
193
194 pthread_attr_init(&attr);
195 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
196 result = pthread_create(&demand_thread, &attr, demand_loop, NULL);
197 if (result)
198 panic("pthread_create(): %s", strerror(result));
199 pthread_attr_destroy(&attr);
200
201 return bootstraps.bootstrap_port;
202 }
203
204 static void
205 init_ports(void)
206 {
207 kern_return_t result;
208
209 /*
210 * This task will become the bootstrap task.
211 */
212 /* Create port set that server loop listens to */
213 result = mach_port_allocate(
214 bootstrap_self,
215 MACH_PORT_RIGHT_PORT_SET,
216 &bootstrap_port_set);
217 if (result != KERN_SUCCESS)
218 panic("port_set_allocate(): %s", mach_error_string(result));
219
220 /* Create demand port set that second thread listens to */
221 result = mach_port_allocate(
222 bootstrap_self,
223 MACH_PORT_RIGHT_PORT_SET,
224 &demand_port_set);
225 if (result != KERN_SUCCESS)
226 panic("port_set_allocate(): %s", mach_error_string(result));
227
228 /* Create notify port and add to server port set */
229 result = mach_port_allocate(
230 bootstrap_self,
231 MACH_PORT_RIGHT_RECEIVE,
232 &notify_port);
233 if (result != KERN_SUCCESS)
234 panic("mach_port_allocate(): %s", mach_error_string(result));
235
236 result = mach_port_move_member(
237 bootstrap_self,
238 notify_port,
239 bootstrap_port_set);
240 if (result != KERN_SUCCESS)
241 panic("mach_port_move_member(): %s", mach_error_string(result));
242
243 /* Create backup port and add to server port set */
244 result = mach_port_allocate(
245 bootstrap_self,
246 MACH_PORT_RIGHT_RECEIVE,
247 &backup_port);
248 if (result != KERN_SUCCESS)
249 panic("mach_port_allocate(): %s", mach_error_string(result));
250
251 result = mach_port_move_member(
252 bootstrap_self,
253 backup_port,
254 bootstrap_port_set);
255 if (result != KERN_SUCCESS)
256 panic("mach_port_move_member(): %s", mach_error_string(result));
257
258 /* Create "self" port and add to server port set */
259 result = mach_port_allocate(
260 bootstrap_self,
261 MACH_PORT_RIGHT_RECEIVE,
262 &bootstraps.bootstrap_port);
263 if (result != KERN_SUCCESS)
264 panic("mach_port_allocate(): %s", mach_error_string(result));
265 result = mach_port_insert_right(
266 bootstrap_self,
267 bootstraps.bootstrap_port,
268 bootstraps.bootstrap_port,
269 MACH_MSG_TYPE_MAKE_SEND);
270 if (result != KERN_SUCCESS)
271 panic("mach_port_insert_right(): %s", mach_error_string(result));
272
273 /* keep the root bootstrap port "active" */
274 bootstraps.requestor_port = bootstraps.bootstrap_port;
275
276 result = mach_port_move_member(
277 bootstrap_self,
278 bootstraps.bootstrap_port,
279 bootstrap_port_set);
280 if (result != KERN_SUCCESS)
281 panic("mach_port_move_member(): %s", mach_error_string(result));
282 }
283
284 boolean_t
285 active_bootstrap(bootstrap_info_t *bootstrap)
286 {
287 return (bootstrap->requestor_port != MACH_PORT_NULL);
288 }
289
290 boolean_t
291 useless_server(server_t *serverp)
292 {
293 return ( !active_bootstrap(serverp->bootstrap) ||
294 !lookup_service_by_server(serverp) ||
295 !serverp->activity);
296 }
297
298 boolean_t
299 active_server(server_t *serverp)
300 {
301 return ( serverp->port ||
302 serverp->task_port || serverp->active_services);
303 }
304
305 static void
306 reap_server(server_t *serverp)
307 {
308 kern_return_t result;
309 pid_t presult;
310 int wstatus;
311
312 /*
313 * Reap our children.
314 */
315 presult = waitpid(serverp->pid, &wstatus, WNOHANG);
316 switch (presult) {
317 case -1:
318 syslog(LOG_DEBUG, "waitpid: cmd = %s: %m", serverp->cmd);
319 break;
320
321 case 0:
322 {
323 /* process must have switched mach tasks */
324 mach_port_t old_port;
325
326 old_port = serverp->task_port;
327 mach_port_deallocate(mach_task_self(), old_port);
328 serverp->task_port = MACH_PORT_NULL;
329
330 result = task_for_pid( mach_task_self(),
331 serverp->pid,
332 &serverp->task_port);
333 if (result != KERN_SUCCESS) {
334 syslog(LOG_INFO, "race getting new server task port for pid[%d]: %s",
335 serverp->pid, mach_error_string(result));
336 break;
337 }
338
339 /* Request dead name notification to tell when new task dies */
340 result = mach_port_request_notification(
341 mach_task_self(),
342 serverp->task_port,
343 MACH_NOTIFY_DEAD_NAME,
344 0,
345 notify_port,
346 MACH_MSG_TYPE_MAKE_SEND_ONCE,
347 &old_port);
348 if (result != KERN_SUCCESS) {
349 syslog(LOG_INFO, "race setting up notification for new server task port for pid[%d]: %s",
350 serverp->pid, mach_error_string(result));
351 break;
352 }
353 return;
354 }
355
356 default:
357 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) {
358 syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited with status: %d",
359 serverp->port, serverp->bootstrap->bootstrap_port,
360 serverp->uid, serverp->cmd, serverp->pid, WEXITSTATUS(wstatus));
361 } else if (WIFSIGNALED(wstatus)) {
362 syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited abnormally: %s",
363 serverp->port, serverp->bootstrap->bootstrap_port,
364 serverp->uid, serverp->cmd, serverp->pid, strsignal(WTERMSIG(wstatus)));
365 }
366 break;
367 }
368
369
370 serverp->pid = 0;
371
372 /*
373 * Release the server task port reference, if we ever
374 * got it in the first place.
375 */
376 if (serverp->task_port != MACH_PORT_NULL) {
377 result = mach_port_deallocate(
378 mach_task_self(),
379 serverp->task_port);
380 if (result != KERN_SUCCESS)
381 syslog(LOG_ERR, "mach_port_deallocate(): %s", mach_error_string(result));
382 serverp->task_port = MACH_PORT_NULL;
383 }
384 }
385
386 static void
387 demand_server(server_t *serverp)
388 {
389 service_t *servicep;
390 kern_return_t result;
391
392 /*
393 * For on-demand servers, make sure that the service ports are
394 * back in on-demand portset. Active service ports should come
395 * back through a PORT_DESTROYED notification. We only have to
396 * worry about the inactive ports that may have been previously
397 * pulled from the set but never checked-in by the server.
398 */
399
400 for ( servicep = FIRST(services)
401 ; !IS_END(servicep, services)
402 ; servicep = NEXT(servicep))
403 {
404 if (serverp == servicep->server && !servicep->isActive) {
405 result = mach_port_move_member(
406 mach_task_self(),
407 servicep->port,
408 demand_port_set);
409 if (result != KERN_SUCCESS)
410 panic("mach_port_move_member(): %s", mach_error_string(result));
411 }
412 }
413 }
414
415 static
416 void dispatch_server(server_t *serverp)
417 {
418 if (!active_server(serverp)) {
419 if (useless_server(serverp) || shutdown_in_progress)
420 delete_server(serverp);
421 else if (serverp->servertype == RESTARTABLE)
422 start_server(serverp);
423 else if (serverp->servertype == DEMAND)
424 demand_server(serverp);
425 }
426 }
427
428 void
429 setup_server(server_t *serverp)
430 {
431 kern_return_t result;
432 mach_port_t old_port;
433
434 /* Allocate privileged port for requests from service */
435 result = mach_port_allocate(mach_task_self(),
436 MACH_PORT_RIGHT_RECEIVE ,
437 &serverp->port);
438 syslog(LOG_INFO, "Allocating port %x for server %s", serverp->port, serverp->cmd);
439 if (result != KERN_SUCCESS)
440 panic("port_allocate(): %s", mach_error_string(result));
441
442 /* Request no-senders notification so we can tell when server dies */
443 result = mach_port_request_notification(mach_task_self(),
444 serverp->port,
445 MACH_NOTIFY_NO_SENDERS,
446 1,
447 serverp->port,
448 MACH_MSG_TYPE_MAKE_SEND_ONCE,
449 &old_port);
450 if (result != KERN_SUCCESS)
451 panic("mach_port_request_notification(): %s", mach_error_string(result));
452
453 /* Add privileged server port to bootstrap port set */
454 result = mach_port_move_member(mach_task_self(),
455 serverp->port,
456 bootstrap_port_set);
457 if (result != KERN_SUCCESS)
458 panic("mach_port_move_member(): %s", mach_error_string(result));
459 }
460
461 pid_t
462 fork_with_bootstrap_port(mach_port_t p)
463 {
464 static pthread_mutex_t forklock = PTHREAD_MUTEX_INITIALIZER;
465 kern_return_t result;
466 pid_t r;
467 size_t i;
468
469 pthread_mutex_lock(&forklock);
470
471 sigprocmask(SIG_BLOCK, &blocked_signals, NULL);
472
473 result = task_set_bootstrap_port(mach_task_self(), p);
474 if (result != KERN_SUCCESS)
475 panic("task_set_bootstrap_port(): %s", mach_error_string(result));
476
477 if (launchd_bootstrap_port != p) {
478 result = mach_port_deallocate(mach_task_self(), p);
479 if (result != KERN_SUCCESS)
480 panic("mach_port_deallocate(): %s", mach_error_string(result));
481 }
482
483 r = fork();
484
485 if (r > 0) {
486 /* Post Tiger:
487 *
488 * We should set the bootstrap back to MACH_PORT_NULL instead
489 * of launchd_bootstrap_port. This will expose rare latent race
490 * condition bugs, given that some programs assume that the PID
491 * 1's bootstrap port is constant. This function clearly
492 * demonstrates that is no longer true.
493 *
494 * Those programs should be calling bootstrap_parent(), and not
495 * task_for_pid(1) followed by a call to get the bootstrap port
496 * on the task.
497 */
498 result = task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port);
499 if (result != KERN_SUCCESS)
500 panic("task_set_bootstrap_port(): %s", mach_error_string(result));
501 } else {
502 for (i = 0; i <= NSIG; i++) {
503 if (sigismember(&blocked_signals, i))
504 signal(i, SIG_DFL);
505 }
506 }
507
508 sigprocmask(SIG_UNBLOCK, &blocked_signals, NULL);
509
510 pthread_mutex_unlock(&forklock);
511
512 return r;
513 }
514
515 static void
516 start_server(server_t *serverp)
517 {
518 kern_return_t result;
519 mach_port_t old_port;
520 int pid;
521
522 /*
523 * Do what's appropriate to get bootstrap port setup in server task
524 */
525 switch (serverp->servertype) {
526
527 case MACHINIT:
528 break;
529
530 case SERVER:
531 case DEMAND:
532 case RESTARTABLE:
533 if (!serverp->port)
534 setup_server(serverp);
535
536 serverp->activity = 0;
537
538 /* Insert a send right */
539 result = mach_port_insert_right(mach_task_self(),
540 serverp->port,
541 serverp->port,
542 MACH_MSG_TYPE_MAKE_SEND);
543 if (result != KERN_SUCCESS)
544 panic("mach_port_insert_right(): %s", mach_error_string(result));
545
546 pid = fork_with_bootstrap_port(serverp->port);
547 if (pid < 0) {
548 syslog(LOG_WARNING, "fork(): %m");
549 } else if (pid == 0) { /* CHILD */
550 exec_server(serverp);
551 exit(EXIT_FAILURE);
552 } else { /* PARENT */
553 syslog(LOG_INFO, "Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]",
554 serverp->port, serverp->bootstrap->bootstrap_port,
555 serverp->uid, serverp->cmd, pid);
556 serverp->pid = pid;
557 result = task_for_pid(
558 mach_task_self(),
559 pid,
560 &serverp->task_port);
561 if (result != KERN_SUCCESS) {
562 syslog(LOG_ERR, "getting server task port(): %s", mach_error_string(result));
563 reap_server(serverp);
564 dispatch_server(serverp);
565 break;
566 }
567
568 /* Request dead name notification to tell when task dies */
569 result = mach_port_request_notification(
570 mach_task_self(),
571 serverp->task_port,
572 MACH_NOTIFY_DEAD_NAME,
573 0,
574 notify_port,
575 MACH_MSG_TYPE_MAKE_SEND_ONCE,
576 &old_port);
577 if (result != KERN_SUCCESS) {
578 syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result));
579 reap_server(serverp);
580 dispatch_server(serverp);
581 }
582 }
583 break;
584 }
585 }
586
587 static void
588 exec_server(server_t *serverp)
589 {
590 char **argv;
591 sigset_t mask;
592
593 /*
594 * Setup environment for server, someday this should be Mach stuff
595 * rather than Unix crud
596 */
597 argv = argvize(serverp->cmd);
598 closelog();
599
600 /*
601 * Set up the audit state for the user (if necessesary).
602 */
603 if (inherited_uid == 0 &&
604 (serverp->auinfo.ai_auid != inherited_uid ||
605 serverp->auinfo.ai_asid != inherited_audit.ai_asid)) {
606 struct passwd *pwd = NULL;
607
608 pwd = getpwuid(serverp->auinfo.ai_auid);
609 if (pwd == NULL) {
610 panic("Disabled server %x bootstrap %x: \"%s\": getpwuid(%d) failed",
611 serverp->port, serverp->bootstrap->bootstrap_port,
612 serverp->cmd, serverp->auinfo.ai_auid);
613
614 } else if (au_user_mask(pwd->pw_name, &serverp->auinfo.ai_mask) != 0) {
615 panic("Disabled server %x bootstrap %x: \"%s\": au_user_mask(%s) failed",
616 serverp->port, serverp->bootstrap->bootstrap_port,
617 serverp->cmd, pwd->pw_name);
618 } else if (setaudit(&serverp->auinfo) != 0)
619 panic("Disabled server %x bootstrap %x: \"%s\": setaudit()",
620 serverp->port, serverp->bootstrap->bootstrap_port,
621 serverp->cmd);
622 }
623
624 if (serverp->uid != inherited_uid)
625 if (setuid(serverp->uid) < 0)
626 panic("Disabled server %x bootstrap %x: \"%s\": setuid(%d): %s",
627 serverp->port, serverp->bootstrap->bootstrap_port,
628 serverp->cmd, serverp->uid, strerror(errno));
629
630 if (setsid() < 0) {
631 /*
632 * We can't keep this from happening, but we shouldn't start
633 * the server not as a process group leader. So, just fake like
634 * there was real activity, and exit the child. If needed,
635 * we'll re-launch it under another pid.
636 */
637 serverp->activity = 1;
638 panic("Temporary failure server %x bootstrap %x: \"%s\": setsid(): %s",
639 serverp->port, serverp->bootstrap->bootstrap_port,
640 serverp->cmd, strerror(errno));
641 }
642
643 sigemptyset(&mask);
644 (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL);
645
646 setpriority(PRIO_PROCESS, 0, 0);
647 execv(argv[0], argv);
648 panic("Disabled server %x bootstrap %x: \"%s\": exec(): %s",
649 serverp->port,
650 serverp->bootstrap->bootstrap_port,
651 serverp->cmd,
652 strerror(errno));
653 }
654
655 static char **
656 argvize(const char *string)
657 {
658 static char *argv[100], args[1000];
659 const char *cp;
660 char *argp, term;
661 unsigned int nargs;
662
663 /*
664 * Convert a command line into an argv for execv
665 */
666 nargs = 0;
667 argp = args;
668
669 for (cp = string; *cp;) {
670 while (isspace(*cp))
671 cp++;
672 term = (*cp == '"') ? *cp++ : '\0';
673 if (nargs < NELEM(argv))
674 argv[nargs++] = argp;
675 while (*cp && (term ? *cp != term : !isspace(*cp))
676 && argp < END_OF(args)) {
677 if (*cp == '\\')
678 cp++;
679 *argp++ = *cp;
680 if (*cp)
681 cp++;
682 }
683 *argp++ = '\0';
684 }
685 argv[nargs] = NULL;
686 return argv;
687 }
688
689 static void *
690 demand_loop(void *arg __attribute__((unused)))
691 {
692 mach_msg_empty_rcv_t dummy;
693 kern_return_t dresult;
694
695
696 for(;;) {
697 mach_port_name_array_t members;
698 mach_msg_type_number_t membersCnt;
699 mach_port_status_t status;
700 mach_msg_type_number_t statusCnt;
701 unsigned int i;
702
703 /*
704 * Receive indication of message on demand service
705 * ports without actually receiving the message (we'll
706 * let the actual server do that.
707 */
708 dresult = mach_msg(
709 &dummy.header,
710 MACH_RCV_MSG|MACH_RCV_LARGE,
711 0,
712 0,
713 demand_port_set,
714 0,
715 MACH_PORT_NULL);
716 if (dresult != MACH_RCV_TOO_LARGE) {
717 syslog(LOG_ERR, "demand_loop: mach_msg(): %s", mach_error_string(dresult));
718 continue;
719 }
720
721 /*
722 * Some port(s) now have messages on them, find out
723 * which ones (there is no indication of which port
724 * triggered in the MACH_RCV_TOO_LARGE indication).
725 */
726 dresult = mach_port_get_set_status(
727 mach_task_self(),
728 demand_port_set,
729 &members,
730 &membersCnt);
731 if (dresult != KERN_SUCCESS) {
732 syslog(LOG_ERR, "demand_loop: mach_port_get_set_status(): %s", mach_error_string(dresult));
733 continue;
734 }
735
736 for (i = 0; i < membersCnt; i++) {
737 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
738 dresult = mach_port_get_attributes(
739 mach_task_self(),
740 members[i],
741 MACH_PORT_RECEIVE_STATUS,
742 (mach_port_info_t)&status,
743 &statusCnt);
744 if (dresult != KERN_SUCCESS) {
745 syslog(LOG_ERR, "demand_loop: mach_port_get_attributes(): %s", mach_error_string(dresult));
746 continue;
747 }
748
749 /*
750 * For each port with messages, take it out of the
751 * demand service portset, and inform the main thread
752 * that it might have to start the server responsible
753 * for it.
754 */
755 if (status.mps_msgcount) {
756 dresult = mach_port_move_member(
757 mach_task_self(),
758 members[i],
759 MACH_PORT_NULL);
760 if (dresult != KERN_SUCCESS) {
761 syslog(LOG_ERR, "demand_loop: mach_port_move_member(): %s", mach_error_string(dresult));
762 continue;
763 }
764 notify_server_loop(members[i]);
765 }
766 }
767
768 dresult = vm_deallocate(
769 mach_task_self(),
770 (vm_address_t) members,
771 (vm_size_t) membersCnt * sizeof(mach_port_name_t));
772 if (dresult != KERN_SUCCESS) {
773 syslog(LOG_ERR, "demand_loop: vm_deallocate(): %s", mach_error_string(dresult));
774 continue;
775 }
776 }
777 return NULL;
778 }
779
780 /*
781 * server_demux -- processes requests off our service port
782 * Also handles notifications
783 */
784
785 static boolean_t
786 server_demux(
787 mach_msg_header_t *Request,
788 mach_msg_header_t *Reply)
789 {
790 bootstrap_info_t *bootstrap;
791 service_t *servicep;
792 server_t *serverp;
793 kern_return_t result;
794 mig_reply_error_t *reply;
795
796 syslog(LOG_DEBUG, "received message on port %x", Request->msgh_local_port);
797
798 reply = (mig_reply_error_t *)Reply;
799
800 /*
801 * Pick off notification messages
802 */
803 if (Request->msgh_local_port == notify_port) {
804 mach_port_name_t np;
805
806 memset(reply, 0, sizeof(*reply));
807 switch (Request->msgh_id) {
808 case MACH_NOTIFY_DEAD_NAME:
809 np = ((mach_dead_name_notification_t *)Request)->not_port;
810 syslog(LOG_DEBUG, "Notified dead name %x", np);
811
812 if (np == inherited_bootstrap_port) {
813 inherited_bootstrap_port = MACH_PORT_NULL;
814 forward_ok = FALSE;
815 }
816
817 /*
818 * Check to see if a subset requestor port was deleted.
819 */
820 while ((bootstrap = lookup_bootstrap_by_req_port(np)) != NULL) {
821 syslog(LOG_DEBUG, "Received dead name notification for bootstrap subset %x requestor port %x",
822 bootstrap->bootstrap_port, bootstrap->requestor_port);
823 mach_port_deallocate(
824 mach_task_self(),
825 bootstrap->requestor_port);
826 bootstrap->requestor_port = MACH_PORT_NULL;
827 deactivate_bootstrap(bootstrap);
828 }
829
830 /*
831 * Check to see if a defined service has gone
832 * away.
833 */
834 while ((servicep = lookup_service_by_port(np)) != NULL) {
835 /*
836 * Port gone, registered service died.
837 */
838 syslog(LOG_DEBUG, "Received dead name notification for service %s "
839 "on bootstrap port %x\n",
840 servicep->name, servicep->bootstrap);
841 syslog(LOG_DEBUG, "Service %s failed - deallocate", servicep->name);
842 delete_service(servicep);
843 }
844
845 /*
846 * Check to see if a launched server task has gone
847 * away.
848 */
849 if ((serverp = lookup_server_by_task_port(np)) != NULL) {
850 /*
851 * Port gone, server died or picked up new task.
852 */
853 syslog(LOG_DEBUG, "Received task death notification for server %s ",
854 serverp->cmd);
855 reap_server(serverp);
856 dispatch_server(serverp);
857 }
858
859 mach_port_deallocate(mach_task_self(), np);
860 reply->RetCode = KERN_SUCCESS;
861 break;
862
863 case MACH_NOTIFY_PORT_DELETED:
864 np = ((mach_port_deleted_notification_t *)Request)->not_port;
865 syslog(LOG_DEBUG, "port deleted notification on 0x%x", np);
866 reply->RetCode = KERN_SUCCESS;
867 break;
868
869 case MACH_NOTIFY_SEND_ONCE:
870 syslog(LOG_DEBUG, "notification send-once right went unused");
871 reply->RetCode = KERN_SUCCESS;
872 break;
873
874 default:
875 syslog(LOG_ERR, "Unexpected notification: %d", Request->msgh_id);
876 reply->RetCode = KERN_FAILURE;
877 break;
878 }
879 }
880
881 else if (Request->msgh_local_port == backup_port) {
882 mach_port_name_t np;
883
884 memset(reply, 0, sizeof(*reply));
885
886 np = ((mach_port_destroyed_notification_t *)Request)->not_port.name;
887 servicep = lookup_service_by_port(np);
888 if (servicep != NULL) {
889 serverp = servicep->server;
890
891 switch (Request->msgh_id) {
892
893 case MACH_NOTIFY_PORT_DESTROYED:
894 /*
895 * Port sent back to us, server died.
896 */
897 syslog(LOG_DEBUG, "Received destroyed notification for service %s",
898 servicep->name);
899 syslog(LOG_DEBUG, "Service %x bootstrap %x backed up: %s",
900 servicep->port, servicep->bootstrap->bootstrap_port,
901 servicep->name);
902 ASSERT(canReceive(servicep->port));
903 servicep->isActive = FALSE;
904 serverp->active_services--;
905 dispatch_server(serverp);
906 reply->RetCode = KERN_SUCCESS;
907 break;
908
909 case DEMAND_REQUEST:
910 /* message reflected over from demand start thread */
911 if (!active_server(serverp))
912 start_server(serverp);
913 reply->RetCode = KERN_SUCCESS;
914 break;
915
916 default:
917 syslog(LOG_DEBUG, "Mysterious backup_port notification %d", Request->msgh_id);
918 reply->RetCode = KERN_FAILURE;
919 break;
920 }
921 } else {
922 syslog(LOG_DEBUG, "Backup_port notification - previously deleted service");
923 reply->RetCode = KERN_FAILURE;
924 }
925 }
926
927 else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
928 mach_port_t ns = Request->msgh_local_port;
929
930 if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) {
931 /*
932 * A server we launched has released his bootstrap
933 * port send right. We won't re-launch him unless
934 * his services came back to roost. But we need to
935 * destroy the bootstrap port for fear of leaking.
936 */
937 syslog(LOG_DEBUG, "server %s dropped server port", serverp->cmd);
938 serverp->port = MACH_PORT_NULL;
939 dispatch_server(serverp);
940 } else if ((bootstrap = lookup_bootstrap_by_port(ns)) != NULL) {
941 /*
942 * The last direct user of a deactivated bootstrap went away.
943 * We can finally free it.
944 */
945 syslog(LOG_DEBUG, "Deallocating bootstrap %x: no more clients", ns);
946 bootstrap->bootstrap_port = MACH_PORT_NULL;
947 deallocate_bootstrap(bootstrap);
948 }
949
950 result = mach_port_mod_refs(
951 mach_task_self(),
952 ns,
953 MACH_PORT_RIGHT_RECEIVE,
954 -1);
955 if (result != KERN_SUCCESS)
956 panic("mach_port_mod_refs(): %s", mach_error_string(result));
957
958 memset(reply, 0, sizeof(*reply));
959 reply->RetCode = KERN_SUCCESS;
960 }
961
962 else { /* must be a service request */
963 syslog(LOG_DEBUG, "Handled request.");
964 return bootstrap_server(Request, Reply);
965 }
966 return TRUE;
967 }
968
969 /*
970 * server_loop -- pick requests off our service port and process them
971 * Also handles notifications
972 */
973 #define bootstrapMaxRequestSize 1024
974 #define bootstrapMaxReplySize 1024
975
976 void *
977 mach_server_loop(void *arg __attribute__((unused)))
978 {
979 mach_msg_return_t mresult;
980
981 for (;;) {
982 mresult = mach_msg_server(
983 server_demux,
984 bootstrapMaxRequestSize,
985 bootstrap_port_set,
986 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)|
987 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
988 if (mresult != MACH_MSG_SUCCESS)
989 syslog(LOG_ERR, "mach_msg_server(): %s", mach_error_string(mresult));
990 }
991 return NULL;
992 }
993
994 bool
995 canReceive(mach_port_t port)
996 {
997 mach_port_type_t p_type;
998 kern_return_t result;
999
1000 result = mach_port_type(mach_task_self(), port, &p_type);
1001 if (result != KERN_SUCCESS) {
1002 syslog(LOG_ERR, "port_type(): %s", mach_error_string(result));
1003 return FALSE;
1004 }
1005 return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0);
1006 }
1007
1008
1009 bool
1010 canSend(mach_port_t port)
1011 {
1012 mach_port_type_t p_type;
1013 kern_return_t result;
1014
1015 result = mach_port_type(mach_task_self(), port, &p_type);
1016 if (result != KERN_SUCCESS) {
1017 syslog(LOG_ERR, "port_type(): %s", mach_error_string(result));
1018 return FALSE;
1019 }
1020 return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0);
1021 }