Formerly sysdeps/mach/hurd/__ioctl.c.~28~
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / ioctl.c
1 /* Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <errno.h>
21 #include <sys/ioctl.h>
22 #include <hurd.h>
23 #include <hurd/fd.h>
24 #include <hurd/signal.h>
25 #include <stdarg.h>
26 #include <mach/notify.h>
27 #include <assert.h>
28
29 /* Symbol set of ioctl handler lists.  If there are user-registered
30    handlers, one of these lists will contain them.  The other lists are
31    handlers built into the library.  The definition of the set comes from
32    hurdioctl.c.  */
33 extern struct 
34   {
35     size_t n;
36     struct ioctl_handler *v[0];
37   } *const _hurd_ioctl_handlers;
38
39
40 #define typesize(type)  (1 << (type))
41
42
43 /* Perform the I/O control operation specified by REQUEST on FD.
44    The actual type and use of ARG and the return value depend on REQUEST.  */
45 int
46 DEFUN(__ioctl, (fd, request),
47       int fd AND unsigned long int request DOTS)
48 {
49   /* Map individual type fields to Mach IPC types.  */
50   static const int mach_types[] =
51     { MACH_MSG_TYPE_CHAR, MACH_MSG_TYPE_INTEGER_16, MACH_MSG_TYPE_INTEGER_32,
52       -1 };
53
54   /* Extract the type information encoded in the request.  */
55   unsigned int type = _IOC_TYPE (request);
56
57   /* Message buffer.  */
58   struct
59     {
60       mig_reply_header_t header;
61       char data[3 * sizeof (mach_msg_type_t) +
62                 _IOT_COUNT0 (type) * typesize (_IOT_TYPE0 (type)) +
63                 _IOT_COUNT1 (type) * typesize (_IOT_TYPE1 (type)) +
64                 _IOT_COUNT2 (type) * typesize (_IOT_TYPE2 (type))];
65     } msg;
66   mach_msg_header_t *const m = &msg.header.Head;
67   mach_msg_type_t *t = &msg.header.RetCodeType;
68   mach_msg_id_t msgid;
69   unsigned int reply_size;
70
71   void *arg;
72
73   error_t err;
74
75   struct hurd_sigstate *ss;
76   int noctty;
77
78   /* Send the RPC already packed up in MSG to IOPORT
79      and decode the return value.  */
80   inline error_t send_rpc (io_t ioport)
81     {
82       error_t err;
83
84       m->msgh_size = (char *) t - (char *) &msg;
85       m->msgh_remote_port = ioport;
86       m->msgh_local_port = __mig_get_reply_port ();
87       m->msgh_seqno = 0;
88       m->msgh_id = msgid;
89       m->msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
90                                      MACH_MSG_TYPE_MAKE_SEND_ONCE);
91       err = HURD_EINTR_RPC (ioport, __mach_msg (m, MACH_SEND_MSG|MACH_RCV_MSG,
92                                                 m->msgh_size, sizeof (msg),
93                                                 m->msgh_local_port,
94                                                 MACH_MSG_TIMEOUT_NONE,
95                                                 MACH_PORT_NULL));
96       switch (err)
97         {
98         case MACH_MSG_SUCCESS:
99           break;
100         case MACH_SEND_INVALID_REPLY:
101         case MACH_RCV_INVALID_NAME:
102           __mig_dealloc_reply_port ();
103         default:
104           return err;
105         }
106
107       if ((m->msgh_bits & MACH_MSGH_BITS_COMPLEX))
108         {
109           /* Allow no ports or VM.  */
110           __mach_msg_destroy (m);
111           /* Want to return a different error below for a different msgid.  */
112           if (m->msgh_id == msgid + 100)
113             return MIG_TYPE_ERROR;
114         }
115
116       if (m->msgh_id != msgid + 100)
117         return (m->msgh_id == MACH_NOTIFY_SEND_ONCE ?
118                 MIG_SERVER_DIED : MIG_REPLY_MISMATCH);
119
120       if (m->msgh_size != reply_size &&
121           m->msgh_size != sizeof (mig_reply_header_t))
122         return MIG_TYPE_ERROR;
123
124       if (*(int *) &msg.header.RetCodeType !=
125           ((union { mach_msg_type_t t; int i; })
126            { t: io2mach_type (1, _IOTS (sizeof msg.header.RetCode)) }).i)
127         return MIG_TYPE_ERROR;
128       return msg.header.RetCode;
129     }
130
131 #define io2mach_type(count, type) \
132   ((mach_msg_type_t) { mach_types[type], typesize (type) * 8, count, 1, 0, 0 })
133
134   va_list ap;
135
136   va_start (ap, request);
137   arg = va_arg (ap, void *);
138   va_end (ap);
139
140   {
141     /* Check for a registered handler for REQUEST.  */
142
143     size_t i;
144     const struct ioctl_handler *h;
145
146     for (i = 0; i < _hurd_ioctl_handlers->n; ++i)
147       for (h = _hurd_ioctl_handlers->v[i]; h != NULL; h = h->next)
148         if (request >= h->first_request && request <= h->last_request)
149           /* This handler groks REQUEST.  Se lo puntamonos.  */
150           return (*h->handler) (fd, request, arg);
151   }
152
153   /* Compute the Mach message ID for the RPC from the group and command
154      parts of the ioctl request.  */
155   msgid = 100000 + ((_IOC_GROUP (request) - 'f') * 4000); /* Base subsystem */
156   /* Because of MiG's poorly chosen algorithm of adding 100 to a request
157      msgid to produce the reply msgid, we cannot just add the command part
158      of the ioctl request to the subsystem base msgid.  For ioctl requests
159      past 99, we must skip blocks of 100 msgids to allow for the reply
160      msgids corresponding to the earlier requests.  */
161   if (_IOC_COMMAND (request) >= 100)
162     msgid += 100;
163   if (_IOC_COMMAND (request) >= 200)
164     msgid += 100;
165   msgid += _IOC_COMMAND (request);
166
167   if (_IOC_INOUT (request) & IOC_IN)
168     {
169       /* Pack an argument into the message buffer.  */
170       void in (unsigned int count, enum __ioctl_datum type)
171         {
172           if (count > 0)
173             {
174               void *p = &t[1];
175               const size_t len = count * typesize ((unsigned int) type);
176               *t = io2mach_type (count, type);
177               memcpy (p, arg, len);
178               arg += len;
179               p += len;
180               p = (void *) (((unsigned long int) p + sizeof (*t) - 1)
181                             & ~(sizeof (*t) - 1));
182               t = p;
183             }
184         }
185
186       /* Pack the argument data.  */
187       in (_IOT_COUNT0 (type), _IOT_TYPE0 (type));
188       in (_IOT_COUNT1 (type), _IOT_TYPE1 (type));
189       in (_IOT_COUNT2 (type), _IOT_TYPE2 (type));
190     }
191
192   /* Compute the expected size of the reply.  There is a standard header
193      consisting of the message header and the reply code.  Then, for out
194      and in/out ioctls, there come the data with their type headers.  */
195   reply_size = sizeof (mig_reply_header_t);
196
197   if (_IOC_INOUT (request) & IOC_OUT)
198     {
199       inline void figure_reply (unsigned int count, enum __ioctl_datum type)
200         {
201           if (count > 0)
202             {
203               /* Add the size of the type and data.  */
204               reply_size += sizeof (mach_msg_type_t) + typesize (type) * count;
205               /* Align it to word size.  */
206               reply_size += sizeof (mach_msg_type_t) - 1;
207               reply_size &= ~(sizeof (mach_msg_type_t) - 1);
208             }
209         }
210       figure_reply (_IOT_COUNT0 (type), _IOT_TYPE0 (type));
211       figure_reply (_IOT_COUNT1 (type), _IOT_TYPE1 (type));
212       figure_reply (_IOT_COUNT2 (type), _IOT_TYPE2 (type));
213     }
214
215   /* Note that fd-write.c implements the same SIGTTOU behavior.
216      Any changes here should be done there as well.  */
217
218   /* Don't use the ctty io port if we are blocking or ignoring SIGTTOU.  */
219   ss = _hurd_self_sigstate ();
220   noctty = (__sigismember (SIGTTOU, &ss->blocked) ||
221             ss->actions[SIGTTOU].sa_handler == SIG_IGN);
222   __mutex_unlock (&ss->lock);
223
224   err = HURD_DPORT_USE
225     (fd,
226      ({
227        const io_t ioport = (noctty && ctty != MACH_PORT_NULL) ? ctty : port;
228        do
229          {
230            /* The actual hair to send the RPC is in the inline `send_rpc'
231               function (above), to avoid horrendous indentation.  */
232            err = send_rpc (ioport);
233            if (ioport == ctty && err == EBACKGROUND)
234              {
235                if (_hurd_orphaned)
236                  /* Our process group is orphaned, so we never generate a
237                     signal; we just fail.  */
238                  err = EIO;
239                else
240                  {
241                    /* Send a SIGTTOU signal to our process group.  */
242                    int restart;
243                    err = __USEPORT (CTTYID, _hurd_sig_post (0, SIGTTOU, port));
244                    /* XXX what to do if error here? */
245                    /* At this point we should have just run the handler for
246                       SIGTTOU or resumed after being stopped.  Now this is
247                       still a "system call", so check to see if we should
248                       restart it.  */
249                    __mutex_lock (&ss->lock);
250                    if (!(ss->actions[SIGTTOU].sa_flags & SA_RESTART))
251                      err = EINTR;
252                    __mutex_unlock (&ss->lock);
253                  }
254              }
255          } while (err == EBACKGROUND);
256        err;
257      }));
258
259   t = (mach_msg_type_t *) msg.data;
260   switch (err)
261     {
262       /* Unpack the message buffer into the argument location.  */
263       int out (unsigned int count, unsigned int type,
264                void *store, void **update)
265         {
266           if (count > 0)
267             {
268               const size_t len = count * typesize (type);
269               union { mach_msg_type_t t; int i; } ipctype;
270               ipctype.t = io2mach_type (count, type);
271               if (*(int *) t != ipctype.i)
272                 return 1;
273               ++t;
274               memcpy (store, t, len);
275               if (update != NULL)
276                 *update += len;
277               t = (void *) (((unsigned long int) t + len + sizeof (*t) - 1)
278                             & ~(sizeof (*t) - 1));
279             }
280           return 0;
281         }
282
283     case 0:
284       if (m->msgh_size != reply_size ||
285           ((_IOC_INOUT (request) & IOC_OUT) &&
286            (out (_IOT_COUNT0 (type), _IOT_TYPE0 (type), arg, &arg) ||
287             out (_IOT_COUNT1 (type), _IOT_TYPE1 (type), arg, &arg) ||
288             out (_IOT_COUNT2 (type), _IOT_TYPE2 (type), arg, &arg))))
289         return __hurd_fail (MIG_TYPE_ERROR);
290       return 0;
291
292     case MIG_BAD_ID:
293     case EOPNOTSUPP:
294       /* The server didn't understand the RPC.  */
295       err = ENOTTY;
296     default:
297       return __hurd_fail (err);
298     }
299 }