|  | /* ioctl commands which must be done in the C library. | 
|  | Copyright (C) 1994-2016 Free Software Foundation, Inc. | 
|  | This file is part of the GNU C Library. | 
|  |  | 
|  | The GNU C Library is free software; you can redistribute it and/or | 
|  | modify it under the terms of the GNU Lesser General Public | 
|  | License as published by the Free Software Foundation; either | 
|  | version 2.1 of the License, or (at your option) any later version. | 
|  |  | 
|  | The GNU C Library is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public | 
|  | License along with the GNU C Library; if not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <hurd.h> | 
|  | #include <hurd/fd.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <hurd/ioctl.h> | 
|  | #include <string.h> | 
|  |  | 
|  |  | 
|  | /* Symbol set of ioctl handler lists.  If there are user-registered | 
|  | handlers, one of these lists will contain them.  The other lists are | 
|  | handlers built into the library.  */ | 
|  | symbol_set_define (_hurd_ioctl_handler_lists) | 
|  |  | 
|  | /* Look up REQUEST in the set of handlers.  */ | 
|  | ioctl_handler_t | 
|  | _hurd_lookup_ioctl_handler (int request) | 
|  | { | 
|  | void *const *ptr; | 
|  | const struct ioctl_handler *h; | 
|  |  | 
|  | /* Mask off the type bits, so that we see requests in a single group as a | 
|  | contiguous block of values.  */ | 
|  | request = _IOC_NOTYPE (request); | 
|  |  | 
|  | for (ptr = symbol_set_first_element (_hurd_ioctl_handler_lists); | 
|  | !symbol_set_end_p (_hurd_ioctl_handler_lists, ptr); | 
|  | ++ptr) | 
|  | for (h = *ptr; h != NULL; h = h->next) | 
|  | if (request >= h->first_request && request <= h->last_request) | 
|  | return h->handler; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #include <fcntl.h> | 
|  |  | 
|  | /* Find out how many bytes may be read from FD without blocking.  */ | 
|  |  | 
|  | static int | 
|  | fioctl (int fd, | 
|  | int request, | 
|  | int *arg) | 
|  | { | 
|  | error_t err; | 
|  |  | 
|  | *(volatile int *) arg = *arg; | 
|  |  | 
|  | switch (request) | 
|  | { | 
|  | default: | 
|  | err = ENOTTY; | 
|  | break; | 
|  |  | 
|  | case FIONREAD: | 
|  | { | 
|  | mach_msg_type_number_t navail; | 
|  | err = HURD_DPORT_USE (fd, __io_readable (port, &navail)); | 
|  | if (!err) | 
|  | *arg = (int) navail; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FIONBIO: | 
|  | err = HURD_DPORT_USE (fd, (*arg ? | 
|  | __io_set_some_openmodes : | 
|  | __io_clear_some_openmodes) | 
|  | (port, O_NONBLOCK)); | 
|  | break; | 
|  |  | 
|  | case FIOASYNC: | 
|  | err = HURD_DPORT_USE (fd, (*arg ? | 
|  | __io_set_some_openmodes : | 
|  | __io_clear_some_openmodes) | 
|  | (port, O_ASYNC)); | 
|  | break; | 
|  |  | 
|  | case FIOSETOWN: | 
|  | err = HURD_DPORT_USE (fd, __io_mod_owner (port, *arg)); | 
|  | break; | 
|  |  | 
|  | case FIOGETOWN: | 
|  | err = HURD_DPORT_USE (fd, __io_get_owner (port, arg)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return err ? __hurd_dfail (fd, err) : 0; | 
|  | } | 
|  |  | 
|  | _HURD_HANDLE_IOCTLS (fioctl, FIOGETOWN, FIONREAD); | 
|  |  | 
|  |  | 
|  | static int | 
|  | fioclex (int fd, | 
|  | int request) | 
|  | { | 
|  | int flag; | 
|  |  | 
|  | switch (request) | 
|  | { | 
|  | default: | 
|  | return __hurd_fail (ENOTTY); | 
|  | case FIOCLEX: | 
|  | flag = FD_CLOEXEC; | 
|  | break; | 
|  | case FIONCLEX: | 
|  | flag = 0; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return __fcntl (fd, F_SETFD, flag); | 
|  | } | 
|  | _HURD_HANDLE_IOCTLS (fioclex, FIOCLEX, FIONCLEX); | 
|  |  | 
|  | #include <hurd/term.h> | 
|  | #include <hurd/tioctl.h> | 
|  |  | 
|  | /* Install a new CTTYID port, atomically updating the dtable appropriately. | 
|  | This consumes the send right passed in.  */ | 
|  |  | 
|  | void | 
|  | _hurd_locked_install_cttyid (mach_port_t cttyid) | 
|  | { | 
|  | mach_port_t old; | 
|  | struct hurd_port *const port = &_hurd_ports[INIT_PORT_CTTYID]; | 
|  | struct hurd_userlink ulink; | 
|  | int i; | 
|  |  | 
|  | /* Install the new cttyid port, and preserve it with a ulink. | 
|  | We unroll the _hurd_port_set + _hurd_port_get here so that | 
|  | there is no window where the cell is unlocked and CTTYID could | 
|  | be changed by another thread.  (We also delay the deallocation | 
|  | of the old port until the end, to minimize the duration of the | 
|  | critical section.) | 
|  |  | 
|  | It is important that changing the cttyid port is only ever done by | 
|  | holding the dtable lock continuously while updating the port cell and | 
|  | re-ctty'ing the dtable; dtable.c assumes we do this.  Otherwise, the | 
|  | pgrp-change notification code in dtable.c has to worry about racing | 
|  | against us here in odd situations.  The one exception to this is | 
|  | setsid, which holds the dtable lock while changing the pgrp and | 
|  | clearing the cttyid port, and then unlocks the dtable lock to allow | 
|  |  | 
|  |  | 
|  | */ | 
|  |  | 
|  | __spin_lock (&port->lock); | 
|  | old = _hurd_userlink_clear (&port->users) ? port->port : MACH_PORT_NULL; | 
|  | port->port = cttyid; | 
|  | cttyid = _hurd_port_locked_get (port, &ulink); | 
|  |  | 
|  | for (i = 0; i < _hurd_dtablesize; ++i) | 
|  | { | 
|  | struct hurd_fd *const d = _hurd_dtable[i]; | 
|  | mach_port_t newctty = MACH_PORT_NULL; | 
|  |  | 
|  | if (d == NULL) | 
|  | /* Nothing to do for an unused descriptor cell.  */ | 
|  | continue; | 
|  |  | 
|  | if (cttyid != MACH_PORT_NULL) | 
|  | /* We do have some controlling tty.  */ | 
|  | HURD_PORT_USE (&d->port, | 
|  | ({ mach_port_t id; | 
|  | /* Get the io object's cttyid port.  */ | 
|  | if (! __term_getctty (port, &id)) | 
|  | { | 
|  | if (id == cttyid /* Is it ours?  */ | 
|  | /* Get the ctty io port.  */ | 
|  | && __term_open_ctty (port, | 
|  | _hurd_pid, _hurd_pgrp, | 
|  | &newctty)) | 
|  | /* XXX it is our ctty but the call failed? */ | 
|  | newctty = MACH_PORT_NULL; | 
|  | __mach_port_deallocate (__mach_task_self (), id); | 
|  | } | 
|  | 0; | 
|  | })); | 
|  |  | 
|  | /* Install the new ctty port.  */ | 
|  | _hurd_port_set (&d->ctty, newctty); | 
|  | } | 
|  |  | 
|  | __mutex_unlock (&_hurd_dtable_lock); | 
|  |  | 
|  | if (old != MACH_PORT_NULL) | 
|  | __mach_port_deallocate (__mach_task_self (), old); | 
|  | _hurd_port_free (port, &ulink, cttyid); | 
|  | } | 
|  |  | 
|  | static void | 
|  | install_ctty (mach_port_t cttyid) | 
|  | { | 
|  | HURD_CRITICAL_BEGIN; | 
|  | __mutex_lock (&_hurd_dtable_lock); | 
|  | _hurd_locked_install_cttyid (cttyid); | 
|  | HURD_CRITICAL_END; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Called when we have received a message saying to use a new ctty ID port.  */ | 
|  |  | 
|  | error_t | 
|  | _hurd_setcttyid (mach_port_t cttyid) | 
|  | { | 
|  | error_t err; | 
|  |  | 
|  | if (cttyid != MACH_PORT_NULL) | 
|  | { | 
|  | /* Give the new send right a user reference. | 
|  | This is a good way to check that it is valid.  */ | 
|  | if (err = __mach_port_mod_refs (__mach_task_self (), cttyid, | 
|  | MACH_PORT_RIGHT_SEND, 1)) | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* Install the port, consuming the reference we just created.  */ | 
|  | install_ctty (cttyid); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static inline error_t | 
|  | do_tiocsctty (io_t port, io_t ctty) | 
|  | { | 
|  | mach_port_t cttyid; | 
|  | error_t err; | 
|  |  | 
|  | if (ctty != MACH_PORT_NULL) | 
|  | /* PORT is already the ctty.  Nothing to do.  */ | 
|  | return 0; | 
|  |  | 
|  | /* Get PORT's cttyid port.  */ | 
|  | err = __term_getctty (port, &cttyid); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | /* Change the terminal's pgrp to ours.  */ | 
|  | err = __tioctl_tiocspgrp (port, _hurd_pgrp); | 
|  | if (err) | 
|  | __mach_port_deallocate (__mach_task_self (), cttyid); | 
|  | else | 
|  | /* Make it our own.  */ | 
|  | install_ctty (cttyid); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* Make FD be the controlling terminal. | 
|  | This function is called for `ioctl (fd, TCIOSCTTY)'.  */ | 
|  |  | 
|  | static int | 
|  | tiocsctty (int fd, | 
|  | int request)		/* Always TIOCSCTTY.  */ | 
|  | { | 
|  | return __hurd_fail (HURD_DPORT_USE (fd, do_tiocsctty (port, ctty))); | 
|  | } | 
|  | _HURD_HANDLE_IOCTL (tiocsctty, TIOCSCTTY); | 
|  |  | 
|  | /* Dissociate from the controlling terminal.  */ | 
|  |  | 
|  | static int | 
|  | tiocnotty (int fd, | 
|  | int request)		/* Always TIOCNOTTY.  */ | 
|  | { | 
|  | mach_port_t fd_cttyid; | 
|  | error_t err; | 
|  |  | 
|  | if (err = HURD_DPORT_USE (fd, __term_getctty (port, &fd_cttyid))) | 
|  | return __hurd_fail (err); | 
|  |  | 
|  | if (__USEPORT (CTTYID, port != fd_cttyid)) | 
|  | err = EINVAL; | 
|  |  | 
|  | __mach_port_deallocate (__mach_task_self (), fd_cttyid); | 
|  |  | 
|  | if (err) | 
|  | return __hurd_fail (err); | 
|  |  | 
|  | /* Clear our cttyid port.  */ | 
|  | install_ctty (MACH_PORT_NULL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | _HURD_HANDLE_IOCTL (tiocnotty, TIOCNOTTY); | 
|  |  | 
|  | #include <hurd/pfinet.h> | 
|  | #include <net/if.h> | 
|  | #include <netinet/in.h> | 
|  |  | 
|  | /* Fill in the buffer IFC->IFC_BUF of length IFC->IFC_LEN with a list | 
|  | of ifr structures, one for each network interface.  */ | 
|  | static int | 
|  | siocgifconf (int fd, int request, struct ifconf *ifc) | 
|  | { | 
|  | error_t err; | 
|  | size_t data_len = ifc->ifc_len; | 
|  | char *data = ifc->ifc_buf; | 
|  |  | 
|  | if (data_len <= 0) | 
|  | return 0; | 
|  |  | 
|  | err = HURD_DPORT_USE (fd, __pfinet_siocgifconf (port, ifc->ifc_len, | 
|  | &data, &data_len)); | 
|  | if (data_len < ifc->ifc_len) | 
|  | ifc->ifc_len = data_len; | 
|  | if (data != ifc->ifc_buf) | 
|  | { | 
|  | memcpy (ifc->ifc_buf, data, ifc->ifc_len); | 
|  | __vm_deallocate (__mach_task_self (), (vm_address_t) data, data_len); | 
|  | } | 
|  | return err ? __hurd_dfail (fd, err) : 0; | 
|  | } | 
|  | _HURD_HANDLE_IOCTL (siocgifconf, SIOCGIFCONF); |