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