(root)/
glibc-2.38/
hurd/
hurdioctl.c
       1  /* ioctl commands which must be done in the C library.
       2     Copyright (C) 1994-2023 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     <https://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  	vm_size_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    mach_msg_type_number_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);