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 #ifndef SQUID_COMMCALLS_H
10 #define SQUID_COMMCALLS_H
11 
12 #include "base/AsyncCall.h"
13 #include "base/AsyncJobCalls.h"
14 #include "comm/Flag.h"
15 #include "comm/forward.h"
16 #include "MasterXaction.h"
17 
18 /* CommCalls implement AsyncCall interface for comm_* callbacks.
19  * The classes cover two call dialer kinds:
20  *     - A C-style call using a function pointer (deprecated);
21  *     - A C++-style call to an AsyncJob child.
22  * and several comm_* callback kinds:
23  *     - accept (IOACB)
24  *     - connect (CNCB)
25  *     - I/O (IOCB)
26  *     - timeout (CTCB)
27  *     - close (CLCB)
28  * and a special callback kind for passing pipe FD, disk FD or fd_table index 'FD' to the handler:
29  *     - FD passing callback (FDECB)
30  */
31 
32 class CommAcceptCbParams;
33 typedef void IOACB(const CommAcceptCbParams &params);
34 
35 typedef void CNCB(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data);
36 typedef void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data);
37 
38 class CommTimeoutCbParams;
39 typedef void CTCB(const CommTimeoutCbParams &params);
40 
41 class CommCloseCbParams;
42 typedef void CLCB(const CommCloseCbParams &params);
43 
44 class FdeCbParams;
45 typedef void FDECB(const FdeCbParams &params);
46 
47 /*
48  * TODO: When there are no function-pointer-based callbacks left, all
49  * this complexity can be removed. Jobs that need comm services will just
50  * implement CommReader, CommWriter, etc. interfaces and receive calls
51  * using general (not comm-specific) AsyncCall code. For now, we have to
52  * allow the caller to create a callback that comm can modify to set
53  * parameters, which is not trivial when the caller type/kind is not
54  * known to comm and there are many kinds of parameters.
55  */
56 
57 /* Comm*CbParams classes below handle callback parameters */
58 
59 // Maintains parameters common to all comm callbacks
60 class CommCommonCbParams
61 {
62 public:
63     CommCommonCbParams(void *aData);
64     CommCommonCbParams(const CommCommonCbParams &params);
65     ~CommCommonCbParams();
66 
67     /// adjust using the current Comm state; returns false to cancel the call
68     // not virtual because callers know dialer type
syncWithComm()69     bool syncWithComm() { return true; }
70 
71     void print(std::ostream &os) const;
72 
73 public:
74     void *data; // cbdata-protected
75 
76     /** The connection which this call pertains to.
77      *  - On accept() calls this is the new client connection.
78      *  - On connect() finished calls this is the newely opened connection.
79      *  - On write calls this is the connection just written to.
80      *  - On read calls this is the connection just read from.
81      *  - On close calls this describes the connection which is now closed.
82      *  - On timeouts this is the connection whose operation timed out.
83      *   + NP: timeouts might also return to the connect/read/write handler with Comm::TIMEOUT.
84      */
85     Comm::ConnectionPointer conn;
86 
87     Comm::Flag flag;  ///< comm layer result status.
88     int xerrno;      ///< The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
89 
90     int fd; ///< FD which the call was about. Set by the async call creator.
91 private:
92     // should not be needed and not yet implemented
93     CommCommonCbParams &operator =(const CommCommonCbParams &params);
94 };
95 
96 // accept parameters
97 class CommAcceptCbParams: public CommCommonCbParams
98 {
99 public:
100     CommAcceptCbParams(void *aData);
101 
102     void print(std::ostream &os) const;
103 
104     /// Transaction which this call is part of.
105     MasterXaction::Pointer xaction;
106 };
107 
108 // connect parameters
109 class CommConnectCbParams: public CommCommonCbParams
110 {
111 public:
112     CommConnectCbParams(void *aData);
113 
114     bool syncWithComm(); // see CommCommonCbParams::syncWithComm
115 };
116 
117 // read/write (I/O) parameters
118 class CommIoCbParams: public CommCommonCbParams
119 {
120 public:
121     CommIoCbParams(void *aData);
122 
123     void print(std::ostream &os) const;
124     bool syncWithComm(); // see CommCommonCbParams::syncWithComm
125 
126 public:
127     char *buf;
128     size_t size;
129 };
130 
131 // close parameters
132 class CommCloseCbParams: public CommCommonCbParams
133 {
134 public:
135     CommCloseCbParams(void *aData);
136 };
137 
138 class CommTimeoutCbParams: public  CommCommonCbParams
139 {
140 public:
141     CommTimeoutCbParams(void *aData);
142 };
143 
144 /// Special Calls parameter, for direct use of an FD without a controlling Comm::Connection
145 /// This is used for pipe() FD with helpers, and internally by Comm when handling some special FD actions.
146 class FdeCbParams: public CommCommonCbParams
147 {
148 public:
149     FdeCbParams(void *aData);
150     // TODO make this a standalone object with FD value and pointer to fde table entry.
151     // that requires all the existing Comm handlers to be updated first though
152 };
153 
154 // Interface to expose comm callback parameters of all comm dialers.
155 // GetCommParams() uses this interface to access comm parameters.
156 template <class Params_>
157 class CommDialerParamsT
158 {
159 public:
160     typedef Params_ Params;
CommDialerParamsT(const Params & io)161     CommDialerParamsT(const Params &io): params(io) {}
162 
163 public:
164     Params params;
165 };
166 
167 // Get comm params of an async comm call
168 template <class Params>
GetCommParams(AsyncCall::Pointer & call)169 Params &GetCommParams(AsyncCall::Pointer &call)
170 {
171     typedef CommDialerParamsT<Params> DialerParams;
172     DialerParams *dp = dynamic_cast<DialerParams*>(call->getDialer());
173     assert(dp);
174     return dp->params;
175 }
176 
177 // All job dialers with comm parameters are merged into one since they
178 // all have exactly one callback argument and differ in Params type only
179 template <class C, class Params_>
180 class CommCbMemFunT: public JobDialer<C>, public CommDialerParamsT<Params_>
181 {
182 public:
183     typedef Params_ Params;
184     typedef void (C::*Method)(const Params &io);
185 
CommCbMemFunT(const CbcPointer<C> & aJob,Method aMeth)186     CommCbMemFunT(const CbcPointer<C> &aJob, Method aMeth): JobDialer<C>(aJob),
187         CommDialerParamsT<Params_>(aJob->toCbdata()),
188         method(aMeth) {}
189 
canDial(AsyncCall & c)190     virtual bool canDial(AsyncCall &c) {
191         return JobDialer<C>::canDial(c) &&
192                this->params.syncWithComm();
193     }
194 
print(std::ostream & os)195     virtual void print(std::ostream &os) const {
196         os << '(';
197         this->params.print(os);
198         os << ')';
199     }
200 
201 public:
202     Method method;
203 
204 protected:
doDial()205     virtual void doDial() { ((&(*this->job))->*method)(this->params); }
206 };
207 
208 // accept (IOACB) dialer
209 class CommAcceptCbPtrFun: public CallDialer,
210     public CommDialerParamsT<CommAcceptCbParams>
211 {
212 public:
213     typedef CommAcceptCbParams Params;
214     typedef RefCount<CommAcceptCbPtrFun> Pointer;
215 
216     CommAcceptCbPtrFun(IOACB *aHandler, const CommAcceptCbParams &aParams);
217     CommAcceptCbPtrFun(const CommAcceptCbPtrFun &o);
218 
219     void dial();
220 
221     virtual void print(std::ostream &os) const;
222 
223 public:
224     IOACB *handler;
225 };
226 
227 // connect (CNCB) dialer
228 class CommConnectCbPtrFun: public CallDialer,
229     public CommDialerParamsT<CommConnectCbParams>
230 {
231 public:
232     typedef CommConnectCbParams Params;
233 
234     CommConnectCbPtrFun(CNCB *aHandler, const Params &aParams);
235     void dial();
236 
237     virtual void print(std::ostream &os) const;
238 
239 public:
240     CNCB *handler;
241 };
242 
243 // read/write (IOCB) dialer
244 class CommIoCbPtrFun: public CallDialer,
245     public CommDialerParamsT<CommIoCbParams>
246 {
247 public:
248     typedef CommIoCbParams Params;
249 
250     CommIoCbPtrFun(IOCB *aHandler, const Params &aParams);
251     void dial();
252 
253     virtual void print(std::ostream &os) const;
254 
255 public:
256     IOCB *handler;
257 };
258 
259 // close (CLCB) dialer
260 class CommCloseCbPtrFun: public CallDialer,
261     public CommDialerParamsT<CommCloseCbParams>
262 {
263 public:
264     typedef CommCloseCbParams Params;
265 
266     CommCloseCbPtrFun(CLCB *aHandler, const Params &aParams);
267     void dial();
268 
269     virtual void print(std::ostream &os) const;
270 
271 public:
272     CLCB *handler;
273 };
274 
275 class CommTimeoutCbPtrFun:public CallDialer,
276     public CommDialerParamsT<CommTimeoutCbParams>
277 {
278 public:
279     typedef CommTimeoutCbParams Params;
280 
281     CommTimeoutCbPtrFun(CTCB *aHandler, const Params &aParams);
282     void dial();
283 
284     virtual void print(std::ostream &os) const;
285 
286 public:
287     CTCB *handler;
288 };
289 
290 /// FD event (FDECB) dialer
291 class FdeCbPtrFun: public CallDialer,
292     public CommDialerParamsT<FdeCbParams>
293 {
294 public:
295     typedef FdeCbParams Params;
296 
297     FdeCbPtrFun(FDECB *aHandler, const Params &aParams);
298     void dial();
299     virtual void print(std::ostream &os) const;
300 
301 public:
302     FDECB *handler;
303 };
304 
305 // AsyncCall to comm handlers implemented as global functions.
306 // The dialer is one of the Comm*CbPtrFunT above
307 // TODO: Get rid of this class by moving canFire() to canDial() method
308 // of dialers.
309 template <class Dialer>
310 class CommCbFunPtrCallT: public AsyncCall
311 {
312 public:
313     typedef RefCount<CommCbFunPtrCallT<Dialer> > Pointer;
314     typedef typename Dialer::Params Params;
315 
316     inline CommCbFunPtrCallT(int debugSection, int debugLevel,
317                              const char *callName, const Dialer &aDialer);
318 
CommCbFunPtrCallT(const CommCbFunPtrCallT & o)319     inline CommCbFunPtrCallT(const CommCbFunPtrCallT &o) :
320         AsyncCall(o.debugSection, o.debugLevel, o.name),
321         dialer(o.dialer) {}
322 
~CommCbFunPtrCallT()323     ~CommCbFunPtrCallT() {}
324 
getDialer()325     virtual CallDialer* getDialer() { return &dialer; }
326 
327 public:
328     Dialer dialer;
329 
330 protected:
331     inline virtual bool canFire();
332     inline virtual void fire();
333 
334 private:
335     CommCbFunPtrCallT & operator=(const CommCbFunPtrCallT &); // not defined. not permitted.
336 };
337 
338 // Conveninece wrapper: It is often easier to call a templated function than
339 // to create a templated class.
340 template <class Dialer>
341 inline
commCbCall(int debugSection,int debugLevel,const char * callName,const Dialer & dialer)342 CommCbFunPtrCallT<Dialer> *commCbCall(int debugSection, int debugLevel,
343                                       const char *callName, const Dialer &dialer)
344 {
345     return new CommCbFunPtrCallT<Dialer>(debugSection, debugLevel, callName,
346                                          dialer);
347 }
348 
349 /* inlined implementation of templated methods */
350 
351 /* CommCbFunPtrCallT */
352 
353 template <class Dialer>
CommCbFunPtrCallT(int aDebugSection,int aDebugLevel,const char * callName,const Dialer & aDialer)354 CommCbFunPtrCallT<Dialer>::CommCbFunPtrCallT(int aDebugSection, int aDebugLevel,
355         const char *callName, const Dialer &aDialer):
356     AsyncCall(aDebugSection, aDebugLevel, callName),
357     dialer(aDialer)
358 {
359 }
360 
361 template <class Dialer>
362 bool
canFire()363 CommCbFunPtrCallT<Dialer>::canFire()
364 {
365     if (!AsyncCall::canFire())
366         return false;
367 
368     if (!cbdataReferenceValid(dialer.params.data))
369         return cancel("callee gone");
370 
371     if (!dialer.params.syncWithComm())
372         return cancel("out of sync w/comm");
373 
374     if (!dialer.handler)
375         return cancel("no callback requested");
376 
377     return true;
378 }
379 
380 template <class Dialer>
381 void
fire()382 CommCbFunPtrCallT<Dialer>::fire()
383 {
384     dialer.dial();
385 }
386 
387 #endif /* SQUID_COMMCALLS_H */
388 
389