]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/helper.h
Simplify appending SBuf to String (#2108)
[thirdparty/squid.git] / src / helper.h
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2025 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_SRC_HELPER_H
12#define SQUID_SRC_HELPER_H
13
14#include "base/AsyncCall.h"
15#include "base/InstanceId.h"
16#include "base/RefCount.h"
17#include "cbdata.h"
18#include "comm/forward.h"
19#include "dlink.h"
20#include "helper/ChildConfig.h"
21#include "helper/forward.h"
22#include "helper/Reply.h"
23#include "helper/Request.h"
24#include "helper/ReservationId.h"
25#include "ip/Address.h"
26#include "sbuf/SBuf.h"
27
28#include <list>
29#include <map>
30#include <queue>
31#include <unordered_map>
32
33class CommTimeoutCbParams;
34class MemBuf;
35class Packable;
36class wordlist;
37
38namespace Helper
39{
40/// Holds the required data to serve a helper request.
41class Xaction {
42 MEMPROXY_CLASS(Helper::Xaction);
43public:
44 Xaction(HLPCB *c, void *d, const char *b): request(c, d, b) {}
45 Helper::Request request;
46 Helper::Reply reply;
47};
48
49class SessionBase;
50
51/**
52 * Managers a set of individual helper processes with a common queue of requests.
53 *
54 * With respect to load, a helper goes through these states (roughly):
55 * idle: no processes are working on requests (and no requests are queued);
56 * normal: some, but not all processes are working (and no requests are queued);
57 * busy: all processes are working (and some requests are possibly queued);
58 * overloaded: a busy helper with more than queue-size requests in the queue.
59 *
60 * A busy helper queues new requests and issues a WARNING every 10 minutes or so.
61 * An overloaded helper either drops new requests or keeps queuing them, depending on
62 * whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs).
63 * If an overloaded helper has been overloaded for 3+ minutes, an attempt to use
64 * it results in on-persistent-overload action, which may kill worker.
65 */
66class Client: public RefCountable
67{
68public:
69 using Pointer = RefCount<Client>;
70
71 /// \returns a newly created instance of the named helper client
72 /// \param name admin-visible helper category (with this process lifetime)
73 static Pointer Make(const char *name);
74
75 virtual ~Client();
76
77 /// \returns next request in the queue, or nil.
78 Xaction *nextRequest();
79
80 /// If possible, submit request. Otherwise, either kill Squid or return false.
81 bool trySubmit(const char *buf, HLPCB * callback, void *data);
82
83 /// Submits a request to the helper or add it to the queue if none of
84 /// the servers is available.
85 void submitRequest(Xaction *);
86
87 /// Dump some stats about the helper state to a Packable object
88 void packStatsInto(Packable *p, const char *label = nullptr) const;
89 /// whether the helper will be in "overloaded" state after one more request
90 /// already overloaded helpers return true
91 bool willOverload() const;
92
93 /// Updates internal statistics and starts new helper processes after
94 /// an unexpected server exit
95 void handleKilledServer(SessionBase *);
96
97 /// Reacts to unexpected helper process death(s), including a failure to start helper(s)
98 /// and an unexpected exit of a previously started helper. \sa handleKilledServer()
99 /// \param madeProgress whether the died helper(s) responded to any requests
100 void handleFewerServers(bool madeProgress);
101
102 /// satisfies all queued requests with a Helper::Unknown answer
103 /// \prec no existing servers will be able to process queued requests
104 /// \sa SessionBase::dropQueued()
105 void dropQueued();
106
107 /// sends transaction response to the transaction initiator
108 void callBack(Xaction &);
109
110 /// Starts required helper process(es).
111 /// The caller is responsible for checking that new processes are needed.
112 virtual void openSessions();
113
114public:
115 wordlist *cmdline = nullptr;
116 dlink_list servers;
117 std::queue<Xaction *> queue;
118 const char *id_name = nullptr;
119 ChildConfig childs; ///< Configuration settings for number running.
120 int ipc_type = 0;
121 Ip::Address addr;
122 unsigned int droppedRequests = 0; ///< requests not sent during helper overload
123 time_t overloadStart = 0; ///< when the helper became overloaded (zero if it is not)
124 time_t last_queue_warn = 0;
125 time_t last_restart = 0;
126 time_t timeout = 0; ///< Requests timeout
127 bool retryTimedOut = false; ///< Whether the timed-out requests must retried
128 bool retryBrokenHelper = false; ///< Whether the requests must retried on BH replies
129 SBuf onTimedOutResponse; ///< The response to use when helper response timedout
130 char eom = '\n'; ///< The char which marks the end of (response) message, normally '\n'
131
132 struct _stats {
133 int requests = 0;
134 int replies = 0;
135 int timedout = 0;
136 int queue_size = 0;
137 int avg_svc_time = 0;
138 } stats;
139
140protected:
141 /// \param name admin-visible helper category (with this process lifetime)
142 explicit Client(const char * const name): id_name(name) {}
143
144 bool queueFull() const;
145 bool overloaded() const;
146 void syncQueueStats();
147 bool prepSubmit();
148 void submit(const char *buf, HLPCB * callback, void *data);
149};
150
151} // namespace Helper
152
153// TODO: Rename to a *Client.
154class statefulhelper: public Helper::Client
155{
156public:
157 using Pointer = RefCount<statefulhelper>;
158 typedef std::unordered_map<Helper::ReservationId, helper_stateful_server *> Reservations;
159
160 ~statefulhelper() override = default;
161
162 static Pointer Make(const char *name);
163
164 /// reserve the given server
165 void reserveServer(helper_stateful_server * srv);
166
167 /// undo reserveServer(), clear the reservation and kick the queue
168 void cancelReservation(const Helper::ReservationId reservation);
169
170 /* Helper::Client API */
171 void openSessions() override;
172
173private:
174 friend void helperStatefulSubmit(const statefulhelper::Pointer &, const char *buf, HLPCB *, void *cbData, const Helper::ReservationId &);
175
176 explicit statefulhelper(const char * const name): Helper::Client(name) {}
177
178 /// \return the previously reserved server (if the reservation is still valid) or nil
179 helper_stateful_server *findServer(const Helper::ReservationId & reservation);
180
181 void submit(const char *buf, HLPCB * callback, void *data, const Helper::ReservationId & reservation);
182 bool trySubmit(const char *buf, HLPCB * callback, void *data, const Helper::ReservationId & reservation);
183
184 ///< reserved servers indexed by reservation IDs
185 Reservations reservations;
186};
187
188namespace Helper
189{
190
191/// represents a single helper process
192class SessionBase: public CbdataParent
193{
194public:
195 ~SessionBase() override;
196
197 /// close handler to handle exited server processes
198 static void HelperServerClosed(SessionBase *);
199
200 /** Closes pipes to the helper safely.
201 * Handles the case where the read and write pipes are the same FD.
202 */
203 void closePipesSafely();
204
205 /** Closes the reading pipe.
206 * If the read and write sockets are the same the write pipe will
207 * also be closed. Otherwise its left open for later handling.
208 */
209 void closeWritePipeSafely();
210
211 // TODO: Teach each child to report its child-specific state instead.
212 /// whether the server is locked for exclusive use by a client
213 virtual bool reserved() = 0;
214
215 /// our creator (parent) object
216 virtual Client &helper() const = 0;
217
218 /// dequeues and sends an Unknown answer to all queued requests
219 virtual void dropQueued();
220
221public:
222 /// Helper program identifier; does not change when contents do,
223 /// including during assignment
224 const InstanceId<SessionBase> index;
225
226 int pid;
227 Ip::Address addr;
228 Comm::ConnectionPointer readPipe;
229 Comm::ConnectionPointer writePipe;
230 void *hIpc;
231
232 char *rbuf;
233 size_t rbuf_sz;
234 size_t roffset;
235
236 struct timeval dispatch_time;
237 struct timeval answer_time;
238
239 dlink_node link;
240
241 struct _helper_flags {
242 bool writing;
243 bool closing;
244 bool shutdown;
245 } flags;
246
247 using Requests = std::list<Xaction *>;
248 Requests requests; ///< requests in order of submission/expiration
249
250 struct {
251 uint64_t uses; //< requests sent to this helper
252 uint64_t replies; //< replies received from this helper
253 uint64_t pending; //< queued lookups waiting to be sent to this helper
254 uint64_t releases; //< times release() has been called on this helper (if stateful)
255 uint64_t timedout; //< requests which timed-out
256 } stats;
257 void initStats();
258};
259
260/// represents a single "stateless helper" process;
261/// supports concurrent helper requests
262class Session: public SessionBase
263{
264 CBDATA_CHILD(Session);
265
266public:
267 uint64_t nextRequestId;
268
269 MemBuf *wqueue;
270 MemBuf *writebuf;
271
272 Client::Pointer parent;
273
274 /// The helper request Xaction object for the current reply .
275 /// A helper reply may be distributed to more than one of the retrieved
276 /// packets from helper. This member stores the Xaction object as long as
277 /// the end-of-message for current reply is not retrieved.
278 Xaction *replyXaction;
279
280 /// Whether to ignore current message, because it is timed-out or other reason
281 bool ignoreToEom;
282
283 // STL says storing std::list iterators is safe when changing the list
284 typedef std::map<uint64_t, Requests::iterator> RequestIndex;
285 RequestIndex requestsIndex; ///< maps request IDs to requests
286
287 ~Session() override;
288
289 /// Search in queue for the request with requestId, return the related
290 /// Xaction object and remove it from queue.
291 /// If concurrency is disabled then the requestId is ignored and the
292 /// Xaction of the next request in queue is retrieved.
293 Xaction *popRequest(int requestId);
294
295 /// Run over the active requests lists and forces a retry, or timedout reply
296 /// or the configured "on timeout response" for timedout requests.
297 void checkForTimedOutRequests(bool const retry);
298
299 /* SessionBase API */
300 bool reserved() override {return false;}
301 void dropQueued() override;
302 Client &helper() const override { return *parent; }
303
304 /// Read timeout handler
305 static void requestTimeout(const CommTimeoutCbParams &io);
306};
307
308} // namespace Helper
309
310// TODO: Rename to a *Session, matching renamed statefulhelper.
311/// represents a single "stateful helper" process;
312/// supports exclusive transaction reservations
313class helper_stateful_server: public Helper::SessionBase
314{
315 CBDATA_CHILD(helper_stateful_server);
316
317public:
318 ~helper_stateful_server() override;
319 void reserve();
320 void clearReservation();
321
322 /* Helper::SessionBase API */
323 bool reserved() override {return reservationId.reserved();}
324 Helper::Client &helper() const override { return *parent; }
325
326 statefulhelper::Pointer parent;
327
328 // Reservations temporary lock the server for an exclusive "client" use. The
329 // client keeps the reservation ID as a proof of her reservation. If a
330 // reservation expires, and the server is reserved for another client, then
331 // the reservation ID presented by the late client will not match ours.
332 Helper::ReservationId reservationId; ///< "confirmation ID" of the last
333 time_t reservationStart; ///< when the last `reservation` was made
334};
335
336void helperSubmit(const Helper::Client::Pointer &, const char *buf, HLPCB *, void *cbData);
337void helperStatefulSubmit(const statefulhelper::Pointer &, const char *buf, HLPCB *, void *cbData, uint64_t reservation);
338void helperShutdown(const Helper::Client::Pointer &);
339void helperStatefulShutdown(const statefulhelper::Pointer &);
340
341#endif /* SQUID_SRC_HELPER_H */
342