xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame^] | 1 | /* ioctl commands which must be done in the C library. |
| 2 | Copyright (C) 1994-2016 Free Software Foundation, Inc. |
| 3 | This file is part of the GNU C Library. |
| 4 | |
| 5 | The GNU C Library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2.1 of the License, or (at your option) any later version. |
| 9 | |
| 10 | The GNU C Library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Lesser General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Lesser General Public |
| 16 | License along with the GNU C Library; if not, see |
| 17 | <http://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #include <hurd.h> |
| 20 | #include <hurd/fd.h> |
| 21 | #include <sys/ioctl.h> |
| 22 | #include <hurd/ioctl.h> |
| 23 | #include <string.h> |
| 24 | |
| 25 | |
| 26 | /* Symbol set of ioctl handler lists. If there are user-registered |
| 27 | handlers, one of these lists will contain them. The other lists are |
| 28 | handlers built into the library. */ |
| 29 | symbol_set_define (_hurd_ioctl_handler_lists) |
| 30 | |
| 31 | /* Look up REQUEST in the set of handlers. */ |
| 32 | ioctl_handler_t |
| 33 | _hurd_lookup_ioctl_handler (int request) |
| 34 | { |
| 35 | void *const *ptr; |
| 36 | const struct ioctl_handler *h; |
| 37 | |
| 38 | /* Mask off the type bits, so that we see requests in a single group as a |
| 39 | contiguous block of values. */ |
| 40 | request = _IOC_NOTYPE (request); |
| 41 | |
| 42 | for (ptr = symbol_set_first_element (_hurd_ioctl_handler_lists); |
| 43 | !symbol_set_end_p (_hurd_ioctl_handler_lists, ptr); |
| 44 | ++ptr) |
| 45 | for (h = *ptr; h != NULL; h = h->next) |
| 46 | if (request >= h->first_request && request <= h->last_request) |
| 47 | return h->handler; |
| 48 | |
| 49 | return NULL; |
| 50 | } |
| 51 | |
| 52 | #include <fcntl.h> |
| 53 | |
| 54 | /* Find out how many bytes may be read from FD without blocking. */ |
| 55 | |
| 56 | static int |
| 57 | fioctl (int fd, |
| 58 | int request, |
| 59 | int *arg) |
| 60 | { |
| 61 | error_t err; |
| 62 | |
| 63 | *(volatile int *) arg = *arg; |
| 64 | |
| 65 | switch (request) |
| 66 | { |
| 67 | default: |
| 68 | err = ENOTTY; |
| 69 | break; |
| 70 | |
| 71 | case FIONREAD: |
| 72 | { |
| 73 | mach_msg_type_number_t navail; |
| 74 | err = HURD_DPORT_USE (fd, __io_readable (port, &navail)); |
| 75 | if (!err) |
| 76 | *arg = (int) navail; |
| 77 | } |
| 78 | break; |
| 79 | |
| 80 | case FIONBIO: |
| 81 | err = HURD_DPORT_USE (fd, (*arg ? |
| 82 | __io_set_some_openmodes : |
| 83 | __io_clear_some_openmodes) |
| 84 | (port, O_NONBLOCK)); |
| 85 | break; |
| 86 | |
| 87 | case FIOASYNC: |
| 88 | err = HURD_DPORT_USE (fd, (*arg ? |
| 89 | __io_set_some_openmodes : |
| 90 | __io_clear_some_openmodes) |
| 91 | (port, O_ASYNC)); |
| 92 | break; |
| 93 | |
| 94 | case FIOSETOWN: |
| 95 | err = HURD_DPORT_USE (fd, __io_mod_owner (port, *arg)); |
| 96 | break; |
| 97 | |
| 98 | case FIOGETOWN: |
| 99 | err = HURD_DPORT_USE (fd, __io_get_owner (port, arg)); |
| 100 | break; |
| 101 | } |
| 102 | |
| 103 | return err ? __hurd_dfail (fd, err) : 0; |
| 104 | } |
| 105 | |
| 106 | _HURD_HANDLE_IOCTLS (fioctl, FIOGETOWN, FIONREAD); |
| 107 | |
| 108 | |
| 109 | static int |
| 110 | fioclex (int fd, |
| 111 | int request) |
| 112 | { |
| 113 | int flag; |
| 114 | |
| 115 | switch (request) |
| 116 | { |
| 117 | default: |
| 118 | return __hurd_fail (ENOTTY); |
| 119 | case FIOCLEX: |
| 120 | flag = FD_CLOEXEC; |
| 121 | break; |
| 122 | case FIONCLEX: |
| 123 | flag = 0; |
| 124 | break; |
| 125 | } |
| 126 | |
| 127 | return __fcntl (fd, F_SETFD, flag); |
| 128 | } |
| 129 | _HURD_HANDLE_IOCTLS (fioclex, FIOCLEX, FIONCLEX); |
| 130 | |
| 131 | #include <hurd/term.h> |
| 132 | #include <hurd/tioctl.h> |
| 133 | |
| 134 | /* Install a new CTTYID port, atomically updating the dtable appropriately. |
| 135 | This consumes the send right passed in. */ |
| 136 | |
| 137 | void |
| 138 | _hurd_locked_install_cttyid (mach_port_t cttyid) |
| 139 | { |
| 140 | mach_port_t old; |
| 141 | struct hurd_port *const port = &_hurd_ports[INIT_PORT_CTTYID]; |
| 142 | struct hurd_userlink ulink; |
| 143 | int i; |
| 144 | |
| 145 | /* Install the new cttyid port, and preserve it with a ulink. |
| 146 | We unroll the _hurd_port_set + _hurd_port_get here so that |
| 147 | there is no window where the cell is unlocked and CTTYID could |
| 148 | be changed by another thread. (We also delay the deallocation |
| 149 | of the old port until the end, to minimize the duration of the |
| 150 | critical section.) |
| 151 | |
| 152 | It is important that changing the cttyid port is only ever done by |
| 153 | holding the dtable lock continuously while updating the port cell and |
| 154 | re-ctty'ing the dtable; dtable.c assumes we do this. Otherwise, the |
| 155 | pgrp-change notification code in dtable.c has to worry about racing |
| 156 | against us here in odd situations. The one exception to this is |
| 157 | setsid, which holds the dtable lock while changing the pgrp and |
| 158 | clearing the cttyid port, and then unlocks the dtable lock to allow |
| 159 | |
| 160 | |
| 161 | */ |
| 162 | |
| 163 | __spin_lock (&port->lock); |
| 164 | old = _hurd_userlink_clear (&port->users) ? port->port : MACH_PORT_NULL; |
| 165 | port->port = cttyid; |
| 166 | cttyid = _hurd_port_locked_get (port, &ulink); |
| 167 | |
| 168 | for (i = 0; i < _hurd_dtablesize; ++i) |
| 169 | { |
| 170 | struct hurd_fd *const d = _hurd_dtable[i]; |
| 171 | mach_port_t newctty = MACH_PORT_NULL; |
| 172 | |
| 173 | if (d == NULL) |
| 174 | /* Nothing to do for an unused descriptor cell. */ |
| 175 | continue; |
| 176 | |
| 177 | if (cttyid != MACH_PORT_NULL) |
| 178 | /* We do have some controlling tty. */ |
| 179 | HURD_PORT_USE (&d->port, |
| 180 | ({ mach_port_t id; |
| 181 | /* Get the io object's cttyid port. */ |
| 182 | if (! __term_getctty (port, &id)) |
| 183 | { |
| 184 | if (id == cttyid /* Is it ours? */ |
| 185 | /* Get the ctty io port. */ |
| 186 | && __term_open_ctty (port, |
| 187 | _hurd_pid, _hurd_pgrp, |
| 188 | &newctty)) |
| 189 | /* XXX it is our ctty but the call failed? */ |
| 190 | newctty = MACH_PORT_NULL; |
| 191 | __mach_port_deallocate (__mach_task_self (), id); |
| 192 | } |
| 193 | 0; |
| 194 | })); |
| 195 | |
| 196 | /* Install the new ctty port. */ |
| 197 | _hurd_port_set (&d->ctty, newctty); |
| 198 | } |
| 199 | |
| 200 | __mutex_unlock (&_hurd_dtable_lock); |
| 201 | |
| 202 | if (old != MACH_PORT_NULL) |
| 203 | __mach_port_deallocate (__mach_task_self (), old); |
| 204 | _hurd_port_free (port, &ulink, cttyid); |
| 205 | } |
| 206 | |
| 207 | static void |
| 208 | install_ctty (mach_port_t cttyid) |
| 209 | { |
| 210 | HURD_CRITICAL_BEGIN; |
| 211 | __mutex_lock (&_hurd_dtable_lock); |
| 212 | _hurd_locked_install_cttyid (cttyid); |
| 213 | HURD_CRITICAL_END; |
| 214 | } |
| 215 | |
| 216 | |
| 217 | /* Called when we have received a message saying to use a new ctty ID port. */ |
| 218 | |
| 219 | error_t |
| 220 | _hurd_setcttyid (mach_port_t cttyid) |
| 221 | { |
| 222 | error_t err; |
| 223 | |
| 224 | if (cttyid != MACH_PORT_NULL) |
| 225 | { |
| 226 | /* Give the new send right a user reference. |
| 227 | This is a good way to check that it is valid. */ |
| 228 | if (err = __mach_port_mod_refs (__mach_task_self (), cttyid, |
| 229 | MACH_PORT_RIGHT_SEND, 1)) |
| 230 | return err; |
| 231 | } |
| 232 | |
| 233 | /* Install the port, consuming the reference we just created. */ |
| 234 | install_ctty (cttyid); |
| 235 | |
| 236 | return 0; |
| 237 | } |
| 238 | |
| 239 | |
| 240 | static inline error_t |
| 241 | do_tiocsctty (io_t port, io_t ctty) |
| 242 | { |
| 243 | mach_port_t cttyid; |
| 244 | error_t err; |
| 245 | |
| 246 | if (ctty != MACH_PORT_NULL) |
| 247 | /* PORT is already the ctty. Nothing to do. */ |
| 248 | return 0; |
| 249 | |
| 250 | /* Get PORT's cttyid port. */ |
| 251 | err = __term_getctty (port, &cttyid); |
| 252 | if (err) |
| 253 | return err; |
| 254 | |
| 255 | /* Change the terminal's pgrp to ours. */ |
| 256 | err = __tioctl_tiocspgrp (port, _hurd_pgrp); |
| 257 | if (err) |
| 258 | __mach_port_deallocate (__mach_task_self (), cttyid); |
| 259 | else |
| 260 | /* Make it our own. */ |
| 261 | install_ctty (cttyid); |
| 262 | |
| 263 | return err; |
| 264 | } |
| 265 | |
| 266 | /* Make FD be the controlling terminal. |
| 267 | This function is called for `ioctl (fd, TCIOSCTTY)'. */ |
| 268 | |
| 269 | static int |
| 270 | tiocsctty (int fd, |
| 271 | int request) /* Always TIOCSCTTY. */ |
| 272 | { |
| 273 | return __hurd_fail (HURD_DPORT_USE (fd, do_tiocsctty (port, ctty))); |
| 274 | } |
| 275 | _HURD_HANDLE_IOCTL (tiocsctty, TIOCSCTTY); |
| 276 | |
| 277 | /* Dissociate from the controlling terminal. */ |
| 278 | |
| 279 | static int |
| 280 | tiocnotty (int fd, |
| 281 | int request) /* Always TIOCNOTTY. */ |
| 282 | { |
| 283 | mach_port_t fd_cttyid; |
| 284 | error_t err; |
| 285 | |
| 286 | if (err = HURD_DPORT_USE (fd, __term_getctty (port, &fd_cttyid))) |
| 287 | return __hurd_fail (err); |
| 288 | |
| 289 | if (__USEPORT (CTTYID, port != fd_cttyid)) |
| 290 | err = EINVAL; |
| 291 | |
| 292 | __mach_port_deallocate (__mach_task_self (), fd_cttyid); |
| 293 | |
| 294 | if (err) |
| 295 | return __hurd_fail (err); |
| 296 | |
| 297 | /* Clear our cttyid port. */ |
| 298 | install_ctty (MACH_PORT_NULL); |
| 299 | |
| 300 | return 0; |
| 301 | } |
| 302 | _HURD_HANDLE_IOCTL (tiocnotty, TIOCNOTTY); |
| 303 | |
| 304 | #include <hurd/pfinet.h> |
| 305 | #include <net/if.h> |
| 306 | #include <netinet/in.h> |
| 307 | |
| 308 | /* Fill in the buffer IFC->IFC_BUF of length IFC->IFC_LEN with a list |
| 309 | of ifr structures, one for each network interface. */ |
| 310 | static int |
| 311 | siocgifconf (int fd, int request, struct ifconf *ifc) |
| 312 | { |
| 313 | error_t err; |
| 314 | size_t data_len = ifc->ifc_len; |
| 315 | char *data = ifc->ifc_buf; |
| 316 | |
| 317 | if (data_len <= 0) |
| 318 | return 0; |
| 319 | |
| 320 | err = HURD_DPORT_USE (fd, __pfinet_siocgifconf (port, ifc->ifc_len, |
| 321 | &data, &data_len)); |
| 322 | if (data_len < ifc->ifc_len) |
| 323 | ifc->ifc_len = data_len; |
| 324 | if (data != ifc->ifc_buf) |
| 325 | { |
| 326 | memcpy (ifc->ifc_buf, data, ifc->ifc_len); |
| 327 | __vm_deallocate (__mach_task_self (), (vm_address_t) data, data_len); |
| 328 | } |
| 329 | return err ? __hurd_dfail (fd, err) : 0; |
| 330 | } |
| 331 | _HURD_HANDLE_IOCTL (siocgifconf, SIOCGIFCONF); |