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