1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 05    Socket Functions */
10 
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/IoCallback.h"
14 #include "comm/Loops.h"
15 #include "comm/Read.h"
16 #include "comm_internal.h"
17 #include "CommCalls.h"
18 #include "Debug.h"
19 #include "fd.h"
20 #include "fde.h"
21 #include "sbuf/SBuf.h"
22 #include "StatCounters.h"
23 
24 // Does comm check this fd for read readiness?
25 // Note that when comm is not monitoring, there can be a pending callback
26 // call, which may resume comm monitoring once fired.
27 bool
MonitorsRead(int fd)28 Comm::MonitorsRead(int fd)
29 {
30     assert(isOpen(fd) && COMMIO_FD_READCB(fd) != NULL);
31     // Being active is usually the same as monitoring because we always
32     // start monitoring the FD when we configure Comm::IoCallback for I/O
33     // and we usually configure Comm::IoCallback for I/O when we starting
34     // monitoring a FD for reading.
35     return COMMIO_FD_READCB(fd)->active();
36 }
37 
38 void
Read(const Comm::ConnectionPointer & conn,AsyncCall::Pointer & callback)39 Comm::Read(const Comm::ConnectionPointer &conn, AsyncCall::Pointer &callback)
40 {
41     // TODO: move comm_read_base() internals into here
42     // when comm_read() char* API is no longer needed
43     comm_read_base(conn, NULL, 0, callback);
44 }
45 
46 /**
47  * Queue a read.
48  * If a buffer is given the callback is scheduled when the read
49  * completes, on error, or on file descriptor close.
50  *
51  * If no buffer (NULL) is given the callback is scheduled when
52  * the socket FD is ready for a read(2)/recv(2).
53  */
54 void
comm_read_base(const Comm::ConnectionPointer & conn,char * buf,int size,AsyncCall::Pointer & callback)55 comm_read_base(const Comm::ConnectionPointer &conn, char *buf, int size, AsyncCall::Pointer &callback)
56 {
57     debugs(5, 5, "comm_read, queueing read for " << conn << "; asynCall " << callback);
58 
59     /* Make sure we are open and not closing */
60     assert(Comm::IsConnOpen(conn));
61     assert(!fd_table[conn->fd].closing());
62     Comm::IoCallback *ccb = COMMIO_FD_READCB(conn->fd);
63 
64     // Make sure we are either not reading or just passively monitoring.
65     // Active/passive conflicts are OK and simply cancel passive monitoring.
66     if (ccb->active()) {
67         // if the assertion below fails, we have an active comm_read conflict
68         assert(fd_table[conn->fd].halfClosedReader != NULL);
69         commStopHalfClosedMonitor(conn->fd);
70         assert(!ccb->active());
71     }
72     ccb->conn = conn;
73 
74     /* Queue the read */
75     ccb->setCallback(Comm::IOCB_READ, callback, (char *)buf, NULL, size);
76     Comm::SetSelect(conn->fd, COMM_SELECT_READ, Comm::HandleRead, ccb, 0);
77 }
78 
79 Comm::Flag
ReadNow(CommIoCbParams & params,SBuf & buf)80 Comm::ReadNow(CommIoCbParams &params, SBuf &buf)
81 {
82     /* Attempt a read */
83     ++ statCounter.syscalls.sock.reads;
84     SBuf::size_type sz = buf.spaceSize();
85     if (params.size > 0 && params.size < sz)
86         sz = params.size;
87     char *inbuf = buf.rawAppendStart(sz);
88     errno = 0;
89     const int retval = FD_READ_METHOD(params.conn->fd, inbuf, sz);
90     params.xerrno = errno;
91 
92     debugs(5, 3, params.conn << ", size " << sz << ", retval " << retval << ", errno " << params.xerrno);
93 
94     if (retval > 0) { // data read most common case
95         buf.rawAppendFinish(inbuf, retval);
96         fd_bytes(params.conn->fd, retval, FD_READ);
97         params.flag = Comm::OK;
98         params.size = retval;
99 
100     } else if (retval == 0) { // remote closure (somewhat less) common
101         // Note - read 0 == socket EOF, which is a valid read.
102         params.flag = Comm::ENDFILE;
103 
104     } else if (retval < 0) { // connection errors are worst-case
105         debugs(5, 3, params.conn << " Comm::COMM_ERROR: " << xstrerr(params.xerrno));
106         if (ignoreErrno(params.xerrno))
107             params.flag =  Comm::INPROGRESS;
108         else
109             params.flag =  Comm::COMM_ERROR;
110     }
111 
112     return params.flag;
113 }
114 
115 /**
116  * Handle an FD which is ready for read(2).
117  *
118  * If there is no provided buffer to fill call the callback.
119  *
120  * Otherwise attempt a read into the provided buffer.
121  * If the read attempt succeeds or fails, call the callback.
122  * Else, wait for another IO notification.
123  */
124 void
HandleRead(int fd,void * data)125 Comm::HandleRead(int fd, void *data)
126 {
127     Comm::IoCallback *ccb = (Comm::IoCallback *) data;
128 
129     assert(data == COMMIO_FD_READCB(fd));
130     assert(ccb->active());
131 
132     // Without a buffer, just call back.
133     // The callee may ReadMore() to get the data.
134     if (!ccb->buf) {
135         ccb->finish(Comm::OK, 0);
136         return;
137     }
138 
139     /* For legacy callers : Attempt a read */
140     // Keep in sync with Comm::ReadNow()!
141     ++ statCounter.syscalls.sock.reads;
142     int xerrno = errno = 0;
143     int retval = FD_READ_METHOD(fd, ccb->buf, ccb->size);
144     xerrno = errno;
145     debugs(5, 3, "FD " << fd << ", size " << ccb->size << ", retval " << retval << ", errno " << xerrno);
146 
147     /* See if we read anything */
148     /* Note - read 0 == socket EOF, which is a valid read */
149     if (retval >= 0) {
150         fd_bytes(fd, retval, FD_READ);
151         ccb->offset = retval;
152         ccb->finish(Comm::OK, 0);
153         return;
154     } else if (retval < 0 && !ignoreErrno(xerrno)) {
155         debugs(5, 3, "comm_read_try: scheduling Comm::COMM_ERROR");
156         ccb->offset = 0;
157         ccb->finish(Comm::COMM_ERROR, xerrno);
158         return;
159     };
160 
161     /* Nope, register for some more IO */
162     Comm::SetSelect(fd, COMM_SELECT_READ, Comm::HandleRead, data, 0);
163 }
164 
165 /**
166  * Cancel a pending read. Assert that we have the right parameters,
167  * and that there are no pending read events!
168  *
169  * XXX: We do not assert that there are no pending read events and
170  * with async calls it becomes even more difficult.
171  * The whole interface should be reworked to do callback->cancel()
172  * instead of searching for places where the callback may be stored and
173  * updating the state of those places.
174  *
175  * AHC Don't call the comm handlers?
176  */
177 void
comm_read_cancel(int fd,IOCB * callback,void * data)178 comm_read_cancel(int fd, IOCB *callback, void *data)
179 {
180     if (!isOpen(fd)) {
181         debugs(5, 4, "fails: FD " << fd << " closed");
182         return;
183     }
184 
185     Comm::IoCallback *cb = COMMIO_FD_READCB(fd);
186     // TODO: is "active" == "monitors FD"?
187     if (!cb->active()) {
188         debugs(5, 4, "fails: FD " << fd << " inactive");
189         return;
190     }
191 
192     typedef CommCbFunPtrCallT<CommIoCbPtrFun> Call;
193     Call *call = dynamic_cast<Call*>(cb->callback.getRaw());
194     if (!call) {
195         debugs(5, 4, "fails: FD " << fd << " lacks callback");
196         return;
197     }
198 
199     call->cancel("old comm_read_cancel");
200 
201     typedef CommIoCbParams Params;
202     const Params &params = GetCommParams<Params>(cb->callback);
203 
204     /* Ok, we can be reasonably sure we won't lose any data here! */
205     assert(call->dialer.handler == callback);
206     assert(params.data == data);
207 
208     /* Delete the callback */
209     cb->cancel("old comm_read_cancel");
210 
211     /* And the IO event */
212     Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
213 }
214 
215 void
ReadCancel(int fd,AsyncCall::Pointer & callback)216 Comm::ReadCancel(int fd, AsyncCall::Pointer &callback)
217 {
218     callback->cancel("comm_read_cancel");
219 
220     if (!isOpen(fd)) {
221         debugs(5, 4, "fails: FD " << fd << " closed");
222         return;
223     }
224 
225     Comm::IoCallback *cb = COMMIO_FD_READCB(fd);
226 
227     if (!cb->active()) {
228         debugs(5, 4, "fails: FD " << fd << " inactive");
229         return;
230     }
231 
232     AsyncCall::Pointer call = cb->callback;
233 
234     /* Ok, we can be reasonably sure we won't lose any data here! */
235     assert(call == callback);
236 
237     /* Delete the callback */
238     cb->cancel("comm_read_cancel");
239 
240     /* And the IO event */
241     Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
242 }
243 
244