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
iopoll_internal(int fdin,int fdout,bool block,bool broken_output)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 = {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
iopoll(int fdin,int fdout,bool block)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
iopoll_input_ok(int fdin)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
iopoll_output_ok(int fdout)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
fwait_for_nonblocking_write(FILE * f)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
fclose_wait(FILE * f)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
fwrite_wait(char const * buf,ssize_t size,FILE * f)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 }
240