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