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