1
/* Replacement for mach_msg used in interruptible Hurd RPCs.
2
Copyright (C) 1995,96,97,98,99,2000,2001,2002,2005
3
Free Software Foundation, Inc.
4
This file is part of the GNU C Library.
6
The GNU C Library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Lesser General Public
8
License as published by the Free Software Foundation; either
9
version 2.1 of the License, or (at your option) any later version.
11
The GNU C Library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Lesser General Public License for more details.
16
You should have received a copy of the GNU Lesser General Public
17
License along with the GNU C Library; if not, write to the Free
18
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22
#include <mach/mig_errors.h>
23
#include <mach/mig_support.h>
24
#include <hurd/signal.h>
29
#ifdef NDR_CHAR_ASCII /* OSF Mach flavors have different names. */
30
# define mig_reply_header_t mig_reply_error_t
34
_hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
35
mach_msg_option_t option,
36
mach_msg_size_t send_size,
37
mach_msg_size_t rcv_size,
39
mach_msg_timeout_t timeout,
43
struct hurd_sigstate *ss;
44
const mach_msg_option_t user_option = option;
45
const mach_msg_timeout_t user_timeout = timeout;
58
mach_msg_header_t header;
59
mig_reply_header_t reply;
62
mach_msg_header_t header;
72
mach_msg_header_t header;
76
union msg *const m = (void *) msg;
77
mach_msg_bits_t msgh_bits;
78
mach_port_t remote_port;
80
struct clobber save_data;
82
if ((option & (MACH_SEND_MSG|MACH_RCV_MSG)) != (MACH_SEND_MSG|MACH_RCV_MSG)
83
|| _hurd_msgport_thread == MACH_PORT_NULL)
85
/* Either this is not an RPC (i.e., only a send or only a receive),
86
so it can't be interruptible; or, the signal thread is not set up
87
yet, so we cannot do the normal signal magic. Do a normal,
88
uninterruptible mach_msg call instead. */
89
return __mach_msg (&m->header, option, send_size, rcv_size, rcv_name,
93
ss = _hurd_self_sigstate ();
95
/* Save state that gets clobbered by an EINTR reply message.
96
We will need to restore it if we want to retry the RPC. */
97
msgh_bits = m->header.msgh_bits;
98
remote_port = m->header.msgh_remote_port;
99
msgid = m->header.msgh_id;
100
assert (rcv_size >= sizeof m->request);
101
save_data = m->request.data;
103
/* Tell the signal thread that we are doing an interruptible RPC on
104
this port. If we get a signal and should return EINTR, the signal
105
thread will set this variable to MACH_PORT_NULL. The RPC might
106
return EINTR when some other thread gets a signal, in which case we
107
want to restart our call. */
108
ss->intr_port = m->header.msgh_remote_port;
110
/* A signal may arrive here, after intr_port is set, but before the
111
mach_msg system call. The signal handler might do an interruptible
112
RPC, and clobber intr_port; then it would not be set properly when we
113
actually did send the RPC, and a later signal wouldn't interrupt that
114
RPC. So, _hurd_setup_sighandler saves intr_port in the sigcontext,
115
and sigreturn restores it. */
120
At all points here (once SS->intr_port is set), the signal thread
121
thinks we are "about to enter the syscall", and might mutate our
122
return-value register. This is bogus.
127
/* We have been cancelled. Don't do an RPC at all. */
128
ss->intr_port = MACH_PORT_NULL;
133
/* Note that the signal trampoline code might modify our OPTION! */
134
err = INTR_MSG_TRAP (msg, option, send_size,
135
rcv_size, rcv_name, timeout, notify);
139
case MACH_RCV_TIMED_OUT:
140
if (user_option & MACH_RCV_TIMEOUT)
141
/* The real user RPC timed out. */
144
/* The operation was supposedly interrupted, but still has
145
not returned. Declare it interrupted. */
148
case MACH_SEND_INTERRUPTED: /* RPC didn't get out. */
149
if (!(option & MACH_SEND_MSG))
151
/* Oh yes, it did! Since we were not doing a message send,
152
this return code cannot have come from the kernel!
153
Instead, it was the signal thread mutating our state to tell
154
us not to enter this RPC. However, we are already in the receive!
155
Since the signal thread thought we weren't in the RPC yet,
156
it didn't do an interrupt_operation.
162
/* These are the other codes that mean a pseudo-receive modified
163
the message buffer and we might need to clean up the port rights. */
164
case MACH_SEND_TIMED_OUT:
165
case MACH_SEND_INVALID_NOTIFY:
166
#ifdef MACH_SEND_NO_NOTIFY
167
case MACH_SEND_NO_NOTIFY:
169
#ifdef MACH_SEND_NOTIFY_IN_PROGRESS
170
case MACH_SEND_NOTIFY_IN_PROGRESS:
172
if (MACH_MSGH_BITS_REMOTE (msg->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND)
174
__mach_port_deallocate (__mach_task_self (), msg->msgh_remote_port);
176
= (MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
177
MACH_MSGH_BITS_LOCAL (msg->msgh_bits))
178
| MACH_MSGH_BITS_OTHER (msg->msgh_bits));
180
if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX)
182
#ifndef MACH_MSG_PORT_DESCRIPTOR
183
/* Check for MOVE_SEND rights in the message. These hold refs
184
that we need to release in case the message is in fact never
185
re-sent later. Since it might in fact be re-sent, we turn
186
these into COPY_SEND's after deallocating the extra user ref;
187
the caller is responsible for still holding a ref to go with
188
the original COPY_SEND right, so the resend copies it again. */
190
mach_msg_type_long_t *ty = (void *) (msg + 1);
191
while ((void *) ty < (void *) msg + msg->msgh_size)
193
mach_msg_type_name_t name;
194
mach_msg_type_size_t size;
195
mach_msg_type_number_t number;
197
inline void clean_ports (mach_port_t *ports, int dealloc)
199
mach_msg_type_number_t i;
202
case MACH_MSG_TYPE_MOVE_SEND:
203
for (i = 0; i < number; i++)
204
__mach_port_deallocate (__mach_task_self (), *ports++);
205
if (ty->msgtl_header.msgt_longform)
206
ty->msgtl_name = MACH_MSG_TYPE_COPY_SEND;
208
ty->msgtl_header.msgt_name = MACH_MSG_TYPE_COPY_SEND;
210
case MACH_MSG_TYPE_COPY_SEND:
211
case MACH_MSG_TYPE_MOVE_RECEIVE:
214
if (MACH_MSG_TYPE_PORT_ANY (name))
215
assert (! "unexpected port type in interruptible RPC");
218
__vm_deallocate (__mach_task_self (),
219
(vm_address_t) ports,
220
number * sizeof (mach_port_t));
223
if (ty->msgtl_header.msgt_longform)
225
name = ty->msgtl_name;
226
size = ty->msgtl_size;
227
number = ty->msgtl_number;
228
ty = (void *) ty + sizeof (mach_msg_type_long_t);
232
name = ty->msgtl_header.msgt_name;
233
size = ty->msgtl_header.msgt_size;
234
number = ty->msgtl_header.msgt_number;
235
ty = (void *) ty + sizeof (mach_msg_type_t);
238
if (ty->msgtl_header.msgt_inline)
240
clean_ports ((void *) ty, 0);
241
/* calculate length of data in bytes, rounding up */
242
ty = (void *) ty + (((((number * size) + 7) >> 3)
243
+ sizeof (mach_msg_type_t) - 1)
244
&~ (sizeof (mach_msg_type_t) - 1));
248
clean_ports (*(void **) ty,
249
ty->msgtl_header.msgt_deallocate);
250
ty = (void *) ty + sizeof (void *);
253
#else /* Untyped Mach IPC flavor. */
254
mach_msg_body_t *body = (void *) (msg + 1);
255
mach_msg_descriptor_t *desc = (void *) (body + 1);
256
mach_msg_descriptor_t *desc_end = desc + body->msgh_descriptor_count;
257
for (; desc < desc_end; ++desc)
258
switch (desc->type.type)
260
case MACH_MSG_PORT_DESCRIPTOR:
261
switch (desc->port.disposition)
263
case MACH_MSG_TYPE_MOVE_SEND:
264
__mach_port_deallocate (mach_task_self (),
266
desc->port.disposition = MACH_MSG_TYPE_COPY_SEND;
268
case MACH_MSG_TYPE_COPY_SEND:
269
case MACH_MSG_TYPE_MOVE_RECEIVE:
272
assert (! "unexpected port type in interruptible RPC");
275
case MACH_MSG_OOL_DESCRIPTOR:
276
if (desc->out_of_line.deallocate)
277
__vm_deallocate (__mach_task_self (),
278
(vm_address_t) desc->out_of_line.address,
279
desc->out_of_line.size);
281
case MACH_MSG_OOL_PORTS_DESCRIPTOR:
282
switch (desc->ool_ports.disposition)
284
case MACH_MSG_TYPE_MOVE_SEND:
287
const mach_port_t *ports = desc->ool_ports.address;
288
for (i = 0; i < desc->ool_ports.count; ++i)
289
__mach_port_deallocate (__mach_task_self (), ports[i]);
290
desc->ool_ports.disposition = MACH_MSG_TYPE_COPY_SEND;
293
case MACH_MSG_TYPE_COPY_SEND:
294
case MACH_MSG_TYPE_MOVE_RECEIVE:
297
assert (! "unexpected port type in interruptible RPC");
299
if (desc->ool_ports.deallocate)
300
__vm_deallocate (__mach_task_self (),
301
(vm_address_t) desc->ool_ports.address,
302
desc->ool_ports.count
303
* sizeof (mach_port_t));
306
assert (! "unexpected descriptor type in interruptible RPC");
313
/* Either the process was stopped and continued,
314
or the server doesn't support interrupt_operation. */
315
if (ss->intr_port != MACH_PORT_NULL)
316
/* If this signal was for us and it should interrupt calls, the
317
signal thread will have cleared SS->intr_port.
318
Since it's not cleared, the signal was for another thread,
319
or SA_RESTART is set. Restart the interrupted call. */
321
/* Make sure we have a valid reply port. The one we were using
322
may have been destroyed by interruption. */
323
m->header.msgh_local_port = rcv_name = __mig_get_reply_port ();
324
m->header.msgh_bits = msgh_bits;
325
option = user_option;
326
timeout = user_timeout;
331
case MACH_RCV_PORT_DIED:
332
/* Server didn't respond to interrupt_operation,
333
so the signal thread destroyed the reply port. */
339
/* The EINTR return indicates cancellation, so clear the flag. */
343
case MACH_RCV_INTERRUPTED: /* RPC sent; no reply. */
344
option &= ~MACH_SEND_MSG; /* Don't send again. */
346
if (ss->intr_port == MACH_PORT_NULL)
348
/* This signal or cancellation was for us. We need to wait for
349
the reply, but not hang forever. */
350
option |= MACH_RCV_TIMEOUT;
351
/* Never decrease the user's timeout. */
352
if (!(user_option & MACH_RCV_TIMEOUT)
353
|| timeout > _hurd_interrupted_rpc_timeout)
354
timeout = _hurd_interrupted_rpc_timeout;
358
option = user_option;
359
timeout = user_timeout;
361
goto message; /* Retry the receive. */
363
case MACH_MSG_SUCCESS:
365
/* We got a reply. Was it EINTR? */
366
#ifdef MACH_MSG_TYPE_BIT
372
{ t: { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8,
373
1, TRUE, FALSE, FALSE, 0 } };
376
if (m->reply.RetCode == EINTR &&
377
m->header.msgh_size == sizeof m->reply &&
378
#ifdef MACH_MSG_TYPE_BIT
379
m->check.type == check.i &&
381
!(m->header.msgh_bits & MACH_MSGH_BITS_COMPLEX))
383
/* It is indeed EINTR. Is the interrupt for us? */
384
if (ss->intr_port != MACH_PORT_NULL)
386
/* Nope; repeat the RPC.
387
XXX Resources moved? */
389
assert (m->header.msgh_id == msgid + 100);
391
/* We know we have a valid reply port, because we just
392
received the EINTR reply on it. Restore it and the
393
other fields in the message header needed for send,
394
since the header now reflects receipt of the reply. */
395
m->header.msgh_local_port = rcv_name;
396
m->header.msgh_remote_port = remote_port;
397
m->header.msgh_id = msgid;
398
m->header.msgh_bits = msgh_bits;
399
/* Restore the two words clobbered by the reply data. */
400
m->request.data = save_data;
402
/* Restore the original mach_msg options.
403
OPTION may have had MACH_RCV_TIMEOUT added,
404
and/or MACH_SEND_MSG removed. */
405
option = user_option;
406
timeout = user_timeout;
408
/* Now we are ready to repeat the original message send. */
412
/* The EINTR return indicates cancellation,
413
so clear the flag. */
419
default: /* Quiet -Wswitch-enum. */
423
ss->intr_port = MACH_PORT_NULL;