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 84    Helper process maintenance */
10 
11 #ifndef SQUID_HELPER_H
12 #define SQUID_HELPER_H
13 
14 #include "base/AsyncCall.h"
15 #include "base/InstanceId.h"
16 #include "cbdata.h"
17 #include "comm/forward.h"
18 #include "dlink.h"
19 #include "helper/ChildConfig.h"
20 #include "helper/forward.h"
21 #include "helper/Reply.h"
22 #include "helper/Request.h"
23 #include "ip/Address.h"
24 #include "sbuf/SBuf.h"
25 
26 #include <list>
27 #include <map>
28 #include <queue>
29 
30 class Packable;
31 class wordlist;
32 
33 namespace Helper
34 {
35 /// Holds the  required data to serve a helper request.
36 class Xaction {
37     MEMPROXY_CLASS(Helper::Xaction);
38 public:
Xaction(HLPCB * c,void * d,const char * b)39     Xaction(HLPCB *c, void *d, const char *b): request(c, d, b) {}
40     Helper::Request request;
41     Helper::Reply reply;
42 };
43 }
44 
45 /**
46  * Managers a set of individual helper processes with a common queue of requests.
47  *
48  * With respect to load, a helper goes through these states (roughly):
49  *   idle:   no processes are working on requests (and no requests are queued);
50  *   normal: some, but not all processes are working (and no requests are queued);
51  *   busy:   all processes are working (and some requests are possibly queued);
52  *   overloaded: a busy helper with more than queue-size requests in the queue.
53  *
54  * A busy helper queues new requests and issues a WARNING every 10 minutes or so.
55  * An overloaded helper either drops new requests or keeps queuing them, depending on
56  *   whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs).
57  * If an overloaded helper has been overloaded for 3+ minutes, an attempt to use
58  *   it results in on-persistent-overload action, which may kill worker.
59  */
60 class helper
61 {
62     CBDATA_CLASS(helper);
63 
64 public:
helper(const char * name)65     inline helper(const char *name) :
66         cmdline(NULL),
67         id_name(name),
68         ipc_type(0),
69         droppedRequests(0),
70         overloadStart(0),
71         last_queue_warn(0),
72         last_restart(0),
73         timeout(0),
74         retryTimedOut(false),
75         retryBrokenHelper(false),
76         eom('\n') {
77         memset(&stats, 0, sizeof(stats));
78     }
79     ~helper();
80 
81     /// \returns next request in the queue, or nil.
82     Helper::Xaction *nextRequest();
83 
84     /// If possible, submit request. Otherwise, either kill Squid or return false.
85     bool trySubmit(const char *buf, HLPCB * callback, void *data);
86 
87     /// Submits a request to the helper or add it to the queue if none of
88     /// the servers is available.
89     void submitRequest(Helper::Xaction *r);
90 
91     /// Dump some stats about the helper state to a Packable object
92     void packStatsInto(Packable *p, const char *label = NULL) const;
93     /// whether the helper will be in "overloaded" state after one more request
94     /// already overloaded helpers return true
95     bool willOverload() const;
96 
97 public:
98     wordlist *cmdline;
99     dlink_list servers;
100     std::queue<Helper::Xaction *> queue;
101     const char *id_name;
102     Helper::ChildConfig childs;    ///< Configuration settings for number running.
103     int ipc_type;
104     Ip::Address addr;
105     unsigned int droppedRequests; ///< requests not sent during helper overload
106     time_t overloadStart; ///< when the helper became overloaded (zero if it is not)
107     time_t last_queue_warn;
108     time_t last_restart;
109     time_t timeout; ///< Requests timeout
110     bool retryTimedOut; ///< Whether the timed-out requests must retried
111     bool retryBrokenHelper; ///< Whether the requests must retried on BH replies
112     SBuf onTimedOutResponse; ///< The response to use when helper response timedout
113     char eom;   ///< The char which marks the end of (response) message, normally '\n'
114 
115     struct _stats {
116         int requests;
117         int replies;
118         int timedout;
119         int queue_size;
120         int avg_svc_time;
121     } stats;
122 
123 protected:
124     friend void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
125     bool queueFull() const;
126     bool overloaded() const;
127     void syncQueueStats();
128     bool prepSubmit();
129     void submit(const char *buf, HLPCB * callback, void *data);
130 };
131 
132 class statefulhelper : public helper
133 {
134     CBDATA_CLASS(statefulhelper);
135 
136 public:
statefulhelper(const char * name)137     inline statefulhelper(const char *name) : helper(name), datapool(NULL) {}
~statefulhelper()138     inline ~statefulhelper() {}
139 
140 public:
141     MemAllocator *datapool;
142 
143 private:
144     friend void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, helper_stateful_server * lastserver);
145     void submit(const char *buf, HLPCB * callback, void *data, helper_stateful_server *lastserver);
146     bool trySubmit(const char *buf, HLPCB * callback, void *data, helper_stateful_server *lastserver);
147 };
148 
149 /**
150  * Fields shared between stateless and stateful helper servers.
151  */
152 class HelperServerBase
153 {
154 public:
155     /** Closes pipes to the helper safely.
156      * Handles the case where the read and write pipes are the same FD.
157      *
158      * \param name displayed for the helper being shutdown if logging an error
159      */
160     void closePipesSafely(const char *name);
161 
162     /** Closes the reading pipe.
163      * If the read and write sockets are the same the write pipe will
164      * also be closed. Otherwise its left open for later handling.
165      *
166      * \param name displayed for the helper being shutdown if logging an error
167      */
168     void closeWritePipeSafely(const char *name);
169 
170 public:
171     /// Helper program identifier; does not change when contents do,
172     ///   including during assignment
173     const InstanceId<HelperServerBase> index;
174     int pid;
175     Ip::Address addr;
176     Comm::ConnectionPointer readPipe;
177     Comm::ConnectionPointer writePipe;
178     void *hIpc;
179 
180     char *rbuf;
181     size_t rbuf_sz;
182     size_t roffset;
183 
184     struct timeval dispatch_time;
185     struct timeval answer_time;
186 
187     dlink_node link;
188 
189     struct _helper_flags {
190         bool writing;
191         bool closing;
192         bool shutdown;
193         bool reserved;
194     } flags;
195 
196     typedef std::list<Helper::Xaction *> Requests;
197     Requests requests; ///< requests in order of submission/expiration
198 
199     struct {
200         uint64_t uses;     //< requests sent to this helper
201         uint64_t replies;  //< replies received from this helper
202         uint64_t pending;  //< queued lookups waiting to be sent to this helper
203         uint64_t releases; //< times release() has been called on this helper (if stateful)
204         uint64_t timedout; //< requests which timed-out
205     } stats;
206     void initStats();
207 };
208 
209 class MemBuf;
210 class CommTimeoutCbParams;
211 
212 class helper_server : public HelperServerBase
213 {
214     CBDATA_CLASS(helper_server);
215 
216 public:
217     uint64_t nextRequestId;
218 
219     MemBuf *wqueue;
220     MemBuf *writebuf;
221 
222     helper *parent;
223 
224     /// The helper request Xaction object for the current reply .
225     /// A helper reply may be distributed to more than one of the retrieved
226     /// packets from helper. This member stores the Xaction object as long as
227     /// the end-of-message for current reply is not retrieved.
228     Helper::Xaction *replyXaction;
229 
230     /// Whether to ignore current message, because it is timed-out or other reason
231     bool ignoreToEom;
232 
233     // STL says storing std::list iterators is safe when changing the list
234     typedef std::map<uint64_t, Requests::iterator> RequestIndex;
235     RequestIndex requestsIndex; ///< maps request IDs to requests
236 
237     /// Search in queue for the request with requestId, return the related
238     /// Xaction object and remove it from queue.
239     /// If concurrency is disabled then the requestId is ignored and the
240     /// Xaction of the next request in queue is retrieved.
241     Helper::Xaction *popRequest(int requestId);
242 
243     /// Run over the active requests lists and forces a retry, or timedout reply
244     /// or the configured "on timeout response" for timedout requests.
245     void checkForTimedOutRequests(bool const retry);
246 
247     /// Read timeout handler
248     static void requestTimeout(const CommTimeoutCbParams &io);
249 };
250 
251 class helper_stateful_server : public HelperServerBase
252 {
253     CBDATA_CLASS(helper_stateful_server);
254 
255 public:
256     /* MemBuf wqueue; */
257     /* MemBuf writebuf; */
258 
259     statefulhelper *parent;
260 
261     void *data;         /* State data used by the calling routines */
262 };
263 
264 /* helper.c */
265 void helperOpenServers(helper * hlp);
266 void helperStatefulOpenServers(statefulhelper * hlp);
267 void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
268 void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, helper_stateful_server * lastserver);
269 void helperShutdown(helper * hlp);
270 void helperStatefulShutdown(statefulhelper * hlp);
271 void helperStatefulReleaseServer(helper_stateful_server * srv);
272 void *helperStatefulServerGetData(helper_stateful_server * srv);
273 
274 #endif /* SQUID_HELPER_H */
275 
276