(root)/
coreutils-9.4/
src/
iopoll.c
       1  /* iopoll.c -- broken pipe detection / non blocking output handling
       2     Copyright (C) 2022 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program 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
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16  
      17     Written by Carl Edquist in collaboration with Arsen Arsenović.  */
      18  
      19  #include <config.h>
      20  
      21  /* poll(2) is needed on AIX (where 'select' gives a readable
      22     event immediately) and Solaris (where 'select' never gave
      23     a readable event).  Also use poll(2) on systems we know work
      24     and/or are already using poll (linux).  */
      25  
      26  #if defined _AIX || defined __sun || defined __APPLE__ || \
      27      defined __linux__ || defined __ANDROID__
      28  # define IOPOLL_USES_POLL 1
      29    /* Check we've not enabled gnulib's poll module
      30       as that will emulate poll() in a way not
      31       currently compatible with our usage.  */
      32  # if defined HAVE_POLL
      33  #  error "gnulib's poll() replacement is currently incompatible"
      34  # endif
      35  #endif
      36  
      37  #if IOPOLL_USES_POLL
      38  # include <poll.h>
      39  #else
      40  # include <sys/select.h>
      41  #endif
      42  
      43  #include "system.h"
      44  #include "assure.h"
      45  #include "iopoll.h"
      46  #include "isapipe.h"
      47  
      48  
      49  /* BROKEN_OUTPUT selects the mode of operation of this function.
      50     If BROKEN_OUTPUT, wait for FDIN to become ready for reading
      51     or FDOUT to become a broken pipe.
      52     If !BROKEN_OUTPUT, wait for FDIN or FDOUT to become ready for writing.
      53     If either of those are -1, then they're not checked.  Set BLOCK to true
      54     to wait for an event, otherwise return the status immediately.
      55     Return 0 if not BLOCKing and there is no event on the requested descriptors.
      56     Return 0 if FDIN can be read() without blocking, or IOPOLL_BROKEN_OUTPUT if
      57     FDOUT becomes a broken pipe. If !BROKEN_OUTPUT return 0 if FDOUT writable.
      58     Otherwise return IOPOLL_ERROR if there is a poll() or select() error.  */
      59  
      60  static int
      61  iopoll_internal (int fdin, int fdout, bool block, bool broken_output)
      62  {
      63    affirm (fdin != -1 || fdout != -1);
      64  
      65  #if IOPOLL_USES_POLL
      66    struct pollfd pfds[2] = {  /* POLLRDBAND needed for illumos, macOS.  */
      67      { .fd = fdin,  .events = POLLIN | POLLRDBAND, .revents = 0 },
      68      { .fd = fdout, .events = POLLRDBAND, .revents = 0 },
      69    };
      70    int check_out_events = POLLERR | POLLHUP | POLLNVAL;
      71    int ret = 0;
      72  
      73    if (! broken_output)
      74      {
      75        pfds[0].events = pfds[1].events = POLLOUT;
      76        check_out_events = POLLOUT;
      77      }
      78  
      79    while (0 <= ret || errno == EINTR)
      80      {
      81        ret = poll (pfds, 2, block ? -1 : 0);
      82  
      83        if (ret < 0)
      84          continue;
      85        if (ret == 0 && ! block)
      86          return 0;
      87        affirm (0 < ret);
      88        if (pfds[0].revents) /* input available or pipe closed indicating EOF; */
      89          return 0;          /* should now be able to read() without blocking  */
      90        if (pfds[1].revents & check_out_events)
      91          return broken_output ? IOPOLL_BROKEN_OUTPUT : 0;
      92      }
      93  
      94  #else  /* fall back to select()-based implementation */
      95  
      96    int nfds = (fdin > fdout ? fdin : fdout) + 1;
      97    int ret = 0;
      98  
      99    if (FD_SETSIZE < nfds)
     100      {
     101        errno = EINVAL;
     102        ret = -1;
     103      }
     104  
     105    /* If fdout has an error condition (like a broken pipe) it will be seen
     106       as ready for reading.  Assumes fdout is not actually readable.  */
     107    while (0 <= ret || errno == EINTR)
     108      {
     109        fd_set fds;
     110        FD_ZERO (&fds);
     111        if (0 <= fdin)
     112          FD_SET (fdin, &fds);
     113        if (0 <= fdout)
     114          FD_SET (fdout, &fds);
     115  
     116        struct timeval delay = { .tv_sec = 0, .tv_usec = 0 };
     117        ret = select (nfds,
     118                      broken_output ? &fds : nullptr,
     119                      broken_output ? nullptr : &fds,
     120                      nullptr, block ? nullptr : &delay);
     121  
     122        if (ret < 0)
     123          continue;
     124        if (ret == 0 && ! block)
     125          return 0;
     126        affirm (0 < ret);
     127        if (0 <= fdin && FD_ISSET (fdin, &fds))    /* input available or EOF; */
     128          return 0;          /* should now be able to read() without blocking */
     129        if (0 <= fdout && FD_ISSET (fdout, &fds))  /* equiv to POLLERR        */
     130          return broken_output ? IOPOLL_BROKEN_OUTPUT : 0;
     131      }
     132  
     133  #endif
     134    return IOPOLL_ERROR;
     135  }
     136  
     137  extern int
     138  iopoll (int fdin, int fdout, bool block)
     139  {
     140    return iopoll_internal (fdin, fdout, block, true);
     141  }
     142  
     143  
     144  
     145  /* Return true if fdin is relevant for iopoll().
     146     An fd is not relevant for iopoll() if it is always ready for reading,
     147     which is the case for a regular file or block device.  */
     148  
     149  extern bool
     150  iopoll_input_ok (int fdin)
     151  {
     152    struct stat st;
     153    bool always_ready = fstat (fdin, &st) == 0
     154                        && (S_ISREG (st.st_mode)
     155                            || S_ISBLK (st.st_mode));
     156    return ! always_ready;
     157  }
     158  
     159  /* Return true if fdout is suitable for iopoll().
     160     Namely, fdout refers to a pipe.  */
     161  
     162  extern bool
     163  iopoll_output_ok (int fdout)
     164  {
     165    return isapipe (fdout) > 0;
     166  }
     167  
     168  #ifdef EWOULDBLOCK
     169  # define IS_EAGAIN(errcode) ((errcode) == EAGAIN || (errcode) == EWOULDBLOCK)
     170  #else
     171  # define IS_EAGAIN(errcode) ((errcode) == EAGAIN)
     172  #endif
     173  
     174  /* Inspect the errno of the previous syscall.
     175     On EAGAIN, wait for the underlying file descriptor to become writable.
     176     Return true, if EAGAIN has been successfully handled. */
     177  
     178  static bool
     179  fwait_for_nonblocking_write (FILE *f)
     180  {
     181    if (! IS_EAGAIN (errno))
     182      /* non-recoverable write error */
     183      return false;
     184  
     185    int fd = fileno (f);
     186    if (fd == -1)
     187      goto fail;
     188  
     189    /* wait for the file descriptor to become writable */
     190    if (iopoll_internal (-1, fd, true, false) != 0)
     191      goto fail;
     192  
     193    /* successfully waited for the descriptor to become writable */
     194    clearerr (f);
     195    return true;
     196  
     197  fail:
     198    errno = EAGAIN;
     199    return false;
     200  }
     201  
     202  
     203  /* wrapper for fclose() that also waits for F if non blocking.  */
     204  
     205  extern bool
     206  fclose_wait (FILE *f)
     207  {
     208    for (;;)
     209      {
     210        if (fflush (f) == 0)
     211          break;
     212  
     213        if (! fwait_for_nonblocking_write (f))
     214          break;
     215      }
     216  
     217    return fclose (f) == 0;
     218  }
     219  
     220  
     221  /* wrapper for fwrite() that also waits for F if non blocking.  */
     222  
     223  extern bool
     224  fwrite_wait (char const *buf, ssize_t size, FILE *f)
     225  {
     226    for (;;)
     227      {
     228        const size_t written = fwrite (buf, 1, size, f);
     229        size -= written;
     230        affirm (size >= 0);
     231        if (size <= 0)  /* everything written */
     232          return true;
     233  
     234        if (! fwait_for_nonblocking_write (f))
     235          return false;
     236  
     237        buf += written;
     238      }
     239  }