]> git.saurik.com Git - apple/xnu.git/blame - libsyscall/mach/mach_msg.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / libsyscall / mach / mach_msg.c
CommitLineData
2d21ac55
A
1/*
2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53
54#include <stdlib.h>
55#include <mach/mach.h>
56#include <mach/boolean.h>
57#include <mach/kern_return.h>
58#include <mach/message.h>
59#include <mach/mig_errors.h>
60#include <mach/vm_statistics.h>
39236c6e
A
61#include <TargetConditionals.h>
62
63extern int proc_importance_assertion_begin_with_msg(mach_msg_header_t * msg, mach_msg_trailer_t * trailer, uint64_t * assertion_handlep);
64extern int proc_importance_assertion_complete(uint64_t assertion_handle);
2d21ac55
A
65
66#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \
67 mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not))
68
69#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
70
71/*
72 * Routine: mach_msg
73 * Purpose:
74 * Send and/or receive a message. If the message operation
75 * is interrupted, and the user did not request an indication
76 * of that fact, then restart the appropriate parts of the
77 * operation.
78 */
79mach_msg_return_t
80mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify)
81 mach_msg_header_t *msg;
82 mach_msg_option_t option;
83 mach_msg_size_t send_size;
84 mach_msg_size_t rcv_size;
85 mach_port_t rcv_name;
86 mach_msg_timeout_t timeout;
87 mach_port_t notify;
88{
89 mach_msg_return_t mr;
90
91 /*
92 * Consider the following cases:
93 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
94 * plus special bits).
95 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
96 * 3) RPC calls with interruptions in one/both halves.
97 *
98 * We refrain from passing the option bits that we implement
99 * to the kernel. This prevents their presence from inhibiting
100 * the kernel's fast paths (when it checks the option value).
101 */
102
103 mr = MACH_MSG_TRAP(msg, option &~ LIBMACH_OPTIONS,
104 send_size, rcv_size, rcv_name,
105 timeout, notify);
106 if (mr == MACH_MSG_SUCCESS)
107 return MACH_MSG_SUCCESS;
108
109 if ((option & MACH_SEND_INTERRUPT) == 0)
110 while (mr == MACH_SEND_INTERRUPTED)
111 mr = MACH_MSG_TRAP(msg,
112 option &~ LIBMACH_OPTIONS,
113 send_size, rcv_size, rcv_name,
114 timeout, notify);
115
116 if ((option & MACH_RCV_INTERRUPT) == 0)
117 while (mr == MACH_RCV_INTERRUPTED)
118 mr = MACH_MSG_TRAP(msg,
119 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
120 0, rcv_size, rcv_name,
121 timeout, notify);
122
123 return mr;
124}
125
126/*
127 * Routine: mach_msg_overwrite
128 * Purpose:
129 * Send and/or receive a message. If the message operation
130 * is interrupted, and the user did not request an indication
131 * of that fact, then restart the appropriate parts of the
132 * operation.
133 *
134 * Distinct send and receive buffers may be specified. If
135 * no separate receive buffer is specified, the msg parameter
136 * will be used for both send and receive operations.
137 *
138 * In addition to a distinct receive buffer, that buffer may
139 * already contain scatter control information to direct the
140 * receiving of the message.
141 */
142mach_msg_return_t
143mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout,
144 notify, rcv_msg, rcv_scatter_size)
145 mach_msg_header_t *msg;
146 mach_msg_option_t option;
147 mach_msg_size_t send_size;
148 mach_msg_size_t rcv_limit;
149 mach_port_t rcv_name;
150 mach_msg_timeout_t timeout;
151 mach_port_t notify;
152 mach_msg_header_t *rcv_msg;
153 mach_msg_size_t rcv_scatter_size;
154{
155 mach_msg_return_t mr;
156
157 /*
158 * Consider the following cases:
159 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
160 * plus special bits).
161 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
162 * 3) RPC calls with interruptions in one/both halves.
163 *
164 * We refrain from passing the option bits that we implement
165 * to the kernel. This prevents their presence from inhibiting
166 * the kernel's fast paths (when it checks the option value).
167 */
168
169 mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
170 send_size, rcv_limit, rcv_name,
171 timeout, notify, rcv_msg, rcv_scatter_size);
172 if (mr == MACH_MSG_SUCCESS)
173 return MACH_MSG_SUCCESS;
174
175 if ((option & MACH_SEND_INTERRUPT) == 0)
176 while (mr == MACH_SEND_INTERRUPTED)
177 mr = mach_msg_overwrite_trap(msg,
178 option &~ LIBMACH_OPTIONS,
179 send_size, rcv_limit, rcv_name,
180 timeout, notify, rcv_msg, rcv_scatter_size);
181
182 if ((option & MACH_RCV_INTERRUPT) == 0)
183 while (mr == MACH_RCV_INTERRUPTED)
184 mr = mach_msg_overwrite_trap(msg,
185 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
186 0, rcv_limit, rcv_name,
187 timeout, notify, rcv_msg, rcv_scatter_size);
188
189 return mr;
190}
191
192
193mach_msg_return_t
194mach_msg_send(mach_msg_header_t *msg)
195{
196 return mach_msg(msg, MACH_SEND_MSG,
197 msg->msgh_size, 0, MACH_PORT_NULL,
198 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
199}
200
201mach_msg_return_t
202mach_msg_receive(mach_msg_header_t *msg)
203{
204 return mach_msg(msg, MACH_RCV_MSG,
205 0, msg->msgh_size, msg->msgh_local_port,
206 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
207}
208
209
210static void
211mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
212{
213 if (MACH_PORT_VALID(port)) switch (type) {
214 case MACH_MSG_TYPE_MOVE_SEND:
215 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
216 /* destroy the send/send-once right */
6d2010ae 217 (void) mach_port_deallocate(mach_task_self_, port);
2d21ac55
A
218 break;
219
220 case MACH_MSG_TYPE_MOVE_RECEIVE:
221 /* destroy the receive right */
6d2010ae 222 (void) mach_port_mod_refs(mach_task_self_, port,
2d21ac55
A
223 MACH_PORT_RIGHT_RECEIVE, -1);
224 break;
225
226 case MACH_MSG_TYPE_MAKE_SEND:
227 /* create a send right and then destroy it */
6d2010ae 228 (void) mach_port_insert_right(mach_task_self_, port,
2d21ac55 229 port, MACH_MSG_TYPE_MAKE_SEND);
6d2010ae 230 (void) mach_port_deallocate(mach_task_self_, port);
2d21ac55
A
231 break;
232
233 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
234 /* create a send-once right and then destroy it */
6d2010ae 235 (void) mach_port_extract_right(mach_task_self_, port,
2d21ac55
A
236 MACH_MSG_TYPE_MAKE_SEND_ONCE,
237 &port, &type);
6d2010ae 238 (void) mach_port_deallocate(mach_task_self_, port);
2d21ac55
A
239 break;
240 }
241}
242
243static void
244mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
245{
246 if (size != 0)
6d2010ae 247 (void) vm_deallocate(mach_task_self_, addr, size);
2d21ac55
A
248}
249
250
251/*
252 * Routine: mach_msg_destroy
253 * Purpose:
254 * mach_msg_destroy is useful in two contexts.
255 *
256 * First, it can deallocate all port rights and
257 * out-of-line memory in a received message.
258 * When a server receives a request it doesn't want,
259 * it needs this functionality.
260 *
261 * Second, it can mimic the side-effects of a msg-send
262 * operation. The effect is as if the message were sent
263 * and then destroyed inside the kernel. When a server
264 * can't send a reply (because the client died),
265 * it needs this functionality.
266 */
267void
268mach_msg_destroy(mach_msg_header_t *msg)
269{
270 mach_msg_bits_t mbits = msg->msgh_bits;
271
272 /*
273 * The msgh_local_port field doesn't hold a port right.
274 * The receive operation consumes the destination port right.
275 */
276
277 mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
278
279 if (mbits & MACH_MSGH_BITS_COMPLEX) {
6d2010ae
A
280 mach_msg_base_t *base;
281 mach_msg_type_number_t count, i;
282 mach_msg_descriptor_t *daddr;
2d21ac55 283
6d2010ae
A
284 base = (mach_msg_base_t *) msg;
285 count = base->body.msgh_descriptor_count;
2d21ac55 286
6d2010ae
A
287 daddr = (mach_msg_descriptor_t *) (base + 1);
288 for (i = 0; i < count; i++) {
289
290 switch (daddr->type.type) {
2d21ac55 291
6d2010ae 292 case MACH_MSG_PORT_DESCRIPTOR: {
2d21ac55
A
293 mach_msg_port_descriptor_t *dsc;
294
295 /*
296 * Destroy port rights carried in the message
297 */
6d2010ae
A
298 dsc = &daddr->port;
299 mach_msg_destroy_port(dsc->name, dsc->disposition);
300 daddr = (mach_msg_descriptor_t *)(dsc + 1);
2d21ac55 301 break;
6d2010ae 302 }
2d21ac55 303
6d2010ae 304 case MACH_MSG_OOL_DESCRIPTOR: {
2d21ac55
A
305 mach_msg_ool_descriptor_t *dsc;
306
307 /*
308 * Destroy memory carried in the message
309 */
6d2010ae 310 dsc = &daddr->out_of_line;
2d21ac55
A
311 if (dsc->deallocate) {
312 mach_msg_destroy_memory((vm_offset_t)dsc->address,
313 dsc->size);
314 }
6d2010ae 315 daddr = (mach_msg_descriptor_t *)(dsc + 1);
2d21ac55 316 break;
6d2010ae 317 }
2d21ac55 318
6d2010ae
A
319 case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: {
320 mach_msg_ool_descriptor_t *dsc;
321
322 /*
323 * Just skip it.
324 */
316670eb 325 dsc = &daddr->out_of_line;
6d2010ae
A
326 daddr = (mach_msg_descriptor_t *)(dsc + 1);
327 break;
328 }
329
330 case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
2d21ac55
A
331 mach_port_t *ports;
332 mach_msg_ool_ports_descriptor_t *dsc;
333 mach_msg_type_number_t j;
334
335 /*
336 * Destroy port rights carried in the message
337 */
6d2010ae 338 dsc = &daddr->ool_ports;
2d21ac55
A
339 ports = (mach_port_t *) dsc->address;
340 for (j = 0; j < dsc->count; j++, ports++) {
341 mach_msg_destroy_port(*ports, dsc->disposition);
342 }
343
344 /*
345 * Destroy memory carried in the message
346 */
347 if (dsc->deallocate) {
348 mach_msg_destroy_memory((vm_offset_t)dsc->address,
349 dsc->count * sizeof(mach_port_t));
350 }
6d2010ae 351 daddr = (mach_msg_descriptor_t *)(dsc + 1);
2d21ac55 352 break;
6d2010ae 353 }
2d21ac55
A
354 }
355 }
356 }
357}
358
359/*
360 * Routine: mach_msg_server_once
361 * Purpose:
362 * A simple generic server function. It allows more flexibility
363 * than mach_msg_server by processing only one message request
364 * and then returning to the user. Note that more in the way
365 * of error codes are returned to the user; specifically, any
366 * failing error from mach_msg calls will be returned
367 * (though errors from the demux routine or the routine it
368 * calls will not be).
369 */
370mach_msg_return_t
371mach_msg_server_once(
372 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
373 mach_msg_size_t max_size,
374 mach_port_t rcv_name,
375 mach_msg_options_t options)
376{
377 mig_reply_error_t *bufRequest, *bufReply;
378 mach_msg_size_t request_size;
379 mach_msg_size_t request_alloc;
380 mach_msg_size_t trailer_alloc;
381 mach_msg_size_t reply_alloc;
382 mach_msg_return_t mr;
383 kern_return_t kr;
6d2010ae 384 mach_port_t self = mach_task_self_;
2d21ac55
A
385
386 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG);
387
388 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
389 request_alloc = round_page(max_size + trailer_alloc);
390
391 request_size = (options & MACH_RCV_LARGE) ?
392 request_alloc : max_size + trailer_alloc;
393
394 reply_alloc = round_page((options & MACH_SEND_TRAILER) ?
395 (max_size + MAX_TRAILER_SIZE) :
396 max_size);
397
398 kr = vm_allocate(self,
399 (vm_address_t *)&bufReply,
400 reply_alloc,
401 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
402 if (kr != KERN_SUCCESS)
403 return kr;
404
405 for (;;) {
406 mach_msg_size_t new_request_alloc;
407
408 kr = vm_allocate(self,
409 (vm_address_t *)&bufRequest,
410 request_alloc,
411 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
412 if (kr != KERN_SUCCESS) {
413 vm_deallocate(self,
414 (vm_address_t)bufReply,
415 reply_alloc);
416 return kr;
417 }
418
419 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
420 0, request_size, rcv_name,
421 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
422
423 if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)))
424 break;
425
426 new_request_alloc = round_page(bufRequest->Head.msgh_size +
427 trailer_alloc);
428 vm_deallocate(self,
429 (vm_address_t) bufRequest,
430 request_alloc);
431 request_size = request_alloc = new_request_alloc;
432 }
433
434 if (mr == MACH_MSG_SUCCESS) {
435 /* we have a request message */
436
437 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
438
439 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
440 if (bufReply->RetCode == MIG_NO_REPLY)
441 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
442 else if ((bufReply->RetCode != KERN_SUCCESS) &&
443 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
444 /* destroy the request - but not the reply port */
445 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
446 mach_msg_destroy(&bufRequest->Head);
447 }
448 }
449
450 /*
451 * We don't want to block indefinitely because the client
452 * isn't receiving messages from the reply port.
453 * If we have a send-once right for the reply port, then
454 * this isn't a concern because the send won't block.
455 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
456 * To avoid falling off the kernel's fast RPC path unnecessarily,
457 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
458 */
459 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
460
461 mr = mach_msg(&bufReply->Head,
462 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
463 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
464 MACH_SEND_MSG|options :
465 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
466 bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
467 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
468
469 if ((mr != MACH_SEND_INVALID_DEST) &&
470 (mr != MACH_SEND_TIMED_OUT))
471 goto done_once;
472 mr = MACH_MSG_SUCCESS;
473 }
474 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
475 mach_msg_destroy(&bufReply->Head);
476 }
477
478 done_once:
479 (void)vm_deallocate(self,
480 (vm_address_t) bufRequest,
481 request_alloc);
482 (void)vm_deallocate(self,
483 (vm_address_t) bufReply,
484 reply_alloc);
485 return mr;
486}
487
488/*
489 * Routine: mach_msg_server
490 * Purpose:
491 * A simple generic server function. Note that changes here
492 * should be considered for duplication above.
493 */
494mach_msg_return_t
495mach_msg_server(
496 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
497 mach_msg_size_t max_size,
498 mach_port_t rcv_name,
499 mach_msg_options_t options)
500{
501 mig_reply_error_t *bufRequest, *bufReply;
502 mach_msg_size_t request_size;
503 mach_msg_size_t new_request_alloc;
504 mach_msg_size_t request_alloc;
505 mach_msg_size_t trailer_alloc;
506 mach_msg_size_t reply_alloc;
507 mach_msg_return_t mr;
508 kern_return_t kr;
6d2010ae 509 mach_port_t self = mach_task_self_;
2d21ac55
A
510
511 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_OVERWRITE);
512
513 reply_alloc = round_page((options & MACH_SEND_TRAILER) ?
514 (max_size + MAX_TRAILER_SIZE) : max_size);
515
516 kr = vm_allocate(self,
517 (vm_address_t *)&bufReply,
518 reply_alloc,
519 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
520 if (kr != KERN_SUCCESS)
521 return kr;
522
523 request_alloc = 0;
524 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
525 new_request_alloc = round_page(max_size + trailer_alloc);
526
527 request_size = (options & MACH_RCV_LARGE) ?
528 new_request_alloc : max_size + trailer_alloc;
529
530 for (;;) {
531 if (request_alloc < new_request_alloc) {
532 request_alloc = new_request_alloc;
533 kr = vm_allocate(self,
534 (vm_address_t *)&bufRequest,
535 request_alloc,
536 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
537 if (kr != KERN_SUCCESS) {
538 vm_deallocate(self,
539 (vm_address_t)bufReply,
540 reply_alloc);
541 return kr;
542 }
543 }
544
545 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
546 0, request_size, rcv_name,
547 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
548
549 while (mr == MACH_MSG_SUCCESS) {
550 /* we have another request message */
551
552 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
553
554 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
555 if (bufReply->RetCode == MIG_NO_REPLY)
556 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
557 else if ((bufReply->RetCode != KERN_SUCCESS) &&
558 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
559 /* destroy the request - but not the reply port */
560 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
561 mach_msg_destroy(&bufRequest->Head);
562 }
563 }
564
565 /*
566 * We don't want to block indefinitely because the client
567 * isn't receiving messages from the reply port.
568 * If we have a send-once right for the reply port, then
569 * this isn't a concern because the send won't block.
570 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
571 * To avoid falling off the kernel's fast RPC path,
572 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
573 */
574 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
575 if (request_alloc == reply_alloc) {
576 mig_reply_error_t *bufTemp;
577
578 mr = mach_msg(
579 &bufReply->Head,
580 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
581 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
582 MACH_SEND_MSG|MACH_RCV_MSG|options :
583 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options,
584 bufReply->Head.msgh_size, request_size, rcv_name,
585 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
586
587 /* swap request and reply */
588 bufTemp = bufRequest;
589 bufRequest = bufReply;
590 bufReply = bufTemp;
591
592 } else {
593 mr = mach_msg_overwrite(
594 &bufReply->Head,
595 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
596 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
597 MACH_SEND_MSG|MACH_RCV_MSG|options :
598 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options,
599 bufReply->Head.msgh_size, request_size, rcv_name,
600 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
601 &bufRequest->Head, 0);
602 }
603
604 if ((mr != MACH_SEND_INVALID_DEST) &&
605 (mr != MACH_SEND_TIMED_OUT))
606 continue;
607 }
608 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
609 mach_msg_destroy(&bufReply->Head);
610
611 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
612 0, request_size, rcv_name,
613 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
614
615 } /* while (mr == MACH_MSG_SUCCESS) */
616
617 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
618 new_request_alloc = round_page(bufRequest->Head.msgh_size +
619 trailer_alloc);
620 request_size = new_request_alloc;
621 vm_deallocate(self,
622 (vm_address_t) bufRequest,
623 request_alloc);
624 continue;
625 }
626
627 break;
628
629 } /* for(;;) */
630
631 (void)vm_deallocate(self,
632 (vm_address_t) bufRequest,
633 request_alloc);
634 (void)vm_deallocate(self,
635 (vm_address_t) bufReply,
636 reply_alloc);
637 return mr;
638}
39236c6e
A
639
640/*
641 * Routine: mach_msg_server_importance
642 * Purpose:
643 * A simple generic server function which handles importance
644 * promotion assertions for adaptive daemons.
645 */
646mach_msg_return_t
647mach_msg_server_importance(
648 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
649 mach_msg_size_t max_size,
650 mach_port_t rcv_name,
651 mach_msg_options_t options)
652{
653 mig_reply_error_t *bufRequest, *bufReply;
654 mach_msg_size_t request_size;
655 mach_msg_size_t new_request_alloc;
656 mach_msg_size_t request_alloc;
657 mach_msg_size_t trailer_alloc;
658 mach_msg_size_t reply_alloc;
659 mach_msg_return_t mr;
660 kern_return_t kr;
661 mach_port_t self = mach_task_self_;
662 int retval = 1;
663 uint64_t token;
664
665 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_OVERWRITE);
666
667 reply_alloc = round_page((options & MACH_SEND_TRAILER) ?
668 (max_size + MAX_TRAILER_SIZE) : max_size);
669
670 kr = vm_allocate(self,
671 (vm_address_t *)&bufReply,
672 reply_alloc,
673 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
674 if (kr != KERN_SUCCESS)
675 return kr;
676
677 request_alloc = 0;
678 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
679 new_request_alloc = round_page(max_size + trailer_alloc);
680
681 request_size = (options & MACH_RCV_LARGE) ?
682 new_request_alloc : max_size + trailer_alloc;
683
684 for (;;) {
685 if (request_alloc < new_request_alloc) {
686 request_alloc = new_request_alloc;
687 kr = vm_allocate(self,
688 (vm_address_t *)&bufRequest,
689 request_alloc,
690 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
691 if (kr != KERN_SUCCESS) {
692 vm_deallocate(self,
693 (vm_address_t)bufReply,
694 reply_alloc);
695 return kr;
696 }
697 }
698
699 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
700 0, request_size, rcv_name,
701 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
702
703 if (mr == MACH_MSG_SUCCESS) {
704 /* we have another request message */
705
706 retval = proc_importance_assertion_begin_with_msg(&bufRequest->Head, NULL, &token);
707 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
708
709 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
710 if (bufReply->RetCode == MIG_NO_REPLY)
711 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
712 else if ((bufReply->RetCode != KERN_SUCCESS) &&
713 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
714 /* destroy the request - but not the reply port */
715 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
716 mach_msg_destroy(&bufRequest->Head);
717 }
718 }
719
720 /*
721 * We don't want to block indefinitely because the client
722 * isn't receiving messages from the reply port.
723 * If we have a send-once right for the reply port, then
724 * this isn't a concern because the send won't block.
725 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
726 * To avoid falling off the kernel's fast RPC path,
727 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
728 */
729 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
730
731 mr = mach_msg(
732 &bufReply->Head,
733 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
734 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
735 MACH_SEND_MSG|options :
736 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
737 bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
738 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
739
740 if ((mr != MACH_SEND_INVALID_DEST) &&
741 (mr != MACH_SEND_TIMED_OUT)) {
742 if (retval == 0)
743 proc_importance_assertion_complete(token);
744 continue;
745 }
746 mr = MACH_MSG_SUCCESS;
747 }
748 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
749 mach_msg_destroy(&bufReply->Head);
750 if (retval == 0)
751 proc_importance_assertion_complete(token);
752
753 } /* if (mr == MACH_MSG_SUCCESS) */
754
755 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
756 new_request_alloc = round_page(bufRequest->Head.msgh_size +
757 trailer_alloc);
758 request_size = new_request_alloc;
759 vm_deallocate(self,
760 (vm_address_t) bufRequest,
761 request_alloc);
762 continue;
763 } else if (mr == MACH_MSG_SUCCESS)
764 continue;
765 else
766 break;
767
768 } /* for(;;) */
769
770 (void)vm_deallocate(self,
771 (vm_address_t) bufRequest,
772 request_alloc);
773 (void)vm_deallocate(self,
774 (vm_address_t) bufReply,
775 reply_alloc);
776 return mr;
777}