]> git.ipfire.org Git - thirdparty/squid.git/blame - src/helper.cc
Fixed dup2() filedescriptor conflicts. yuck.
[thirdparty/squid.git] / src / helper.cc
CommitLineData
74addf6c 1#include "squid.h"
2
3#define HELPER_MAX_ARGS 64
4
5static PF helperHandleRead;
1f5f60dd 6static PF helperServerFree;
74addf6c 7static void Enqueue(helper * hlp, helper_request *);
8static helper_request *Dequeue(helper * hlp);
9static helper_server *GetFirstAvailable(helper * hlp);
10static void helperDispatch(helper_server * srv, helper_request * r);
11static void helperKickQueue(helper * hlp);
12static void helperRequestFree(helper_request * r);
13
14
15void
16helperOpenServers(helper * hlp)
17{
18 char *s;
19 char *progname;
20 char *shortname;
21 char *procname;
22 char *args[HELPER_MAX_ARGS];
23 char fd_note_buf[FD_DESC_SZ];
24 helper_server *srv;
25 int nargs = 0;
26 int k;
27 int x;
28 int rfd;
29 int wfd;
30 wordlist *w;
31 if (hlp->cmdline == NULL)
32 return;
33 progname = hlp->cmdline->key;
74addf6c 34 if ((s = strrchr(progname, '/')))
35 shortname = xstrdup(s + 1);
36 else
37 shortname = xstrdup(progname);
38 debug(29, 1) ("helperOpenServers: Starting %d '%s' processes\n",
39 hlp->n_to_start, shortname);
40 procname = xmalloc(strlen(shortname) + 3);
41 snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
42 args[nargs++] = procname;
43 for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
44 args[nargs++] = w->key;
45 args[nargs++] = NULL;
46 assert(nargs <= HELPER_MAX_ARGS);
47 for (k = 0; k < hlp->n_to_start; k++) {
c68e9c6b 48 getCurrentTime();
74addf6c 49 rfd = wfd = -1;
50 x = ipcCreate(hlp->ipc_type,
51 progname,
52 args,
53 shortname,
54 &rfd,
55 &wfd);
56 if (x < 0) {
57 debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
58 continue;
59 }
60 hlp->n_running++;
c68e9c6b 61 srv = memAllocate(MEM_HELPER_SERVER);
62 cbdataAdd(srv, MEM_HELPER_SERVER);
74addf6c 63 srv->flags.alive = 1;
64 srv->index = k;
65 srv->rfd = rfd;
66 srv->wfd = wfd;
67 srv->buf = memAllocate(MEM_8K_BUF);
68 srv->buf_sz = 8192;
69 srv->offset = 0;
70 srv->parent = hlp;
1f5f60dd 71 cbdataLock(hlp); /* lock because of the parent backlink */
74addf6c 72 dlinkAddTail(srv, &srv->link, &hlp->servers);
73 if (rfd == wfd) {
74 snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
75 fd_note(rfd, fd_note_buf);
76 } else {
77 snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
78 fd_note(rfd, fd_note_buf);
79 snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
80 fd_note(wfd, fd_note_buf);
81 }
82 commSetNonBlocking(rfd);
83 if (wfd != rfd)
84 commSetNonBlocking(wfd);
1f5f60dd 85 comm_add_close_handler(rfd, helperServerFree, srv);
74addf6c 86 }
87 safe_free(shortname);
88 safe_free(procname);
838b993c 89 helperKickQueue(hlp);
74addf6c 90}
91
92void
93helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
94{
c68e9c6b 95 helper_request *r = memAllocate(MEM_HELPER_REQUEST);
74addf6c 96 helper_server *srv;
5b5f9257 97 if (hlp == NULL) {
7d47d8e6 98 debug(29, 3) ("helperSubmit: hlp == NULL\n");
5b5f9257 99 callback(data, NULL);
100 return;
101 }
74addf6c 102 r->callback = callback;
103 r->data = data;
104 r->buf = xstrdup(buf);
105 cbdataLock(r->data);
106 if ((srv = GetFirstAvailable(hlp)))
107 helperDispatch(srv, r);
108 else
109 Enqueue(hlp, r);
110}
111
112void
113helperStats(StoreEntry * sentry, helper * hlp)
114{
115 helper_server *srv;
116 dlink_node *link;
117 storeAppendPrintf(sentry, "number running: %d of %d\n",
118 hlp->n_running, hlp->n_to_start);
119 storeAppendPrintf(sentry, "requests sent: %d\n",
120 hlp->stats.requests);
121 storeAppendPrintf(sentry, "replies received: %d\n",
122 hlp->stats.replies);
123 storeAppendPrintf(sentry, "queue length: %d\n",
124 hlp->stats.queue_size);
125 storeAppendPrintf(sentry, "avg service time: %d msec\n",
126 hlp->stats.avg_svc_time);
127 storeAppendPrintf(sentry, "\n");
128 storeAppendPrintf(sentry, "%7s\t%7s\t%11s\t%s\t%7s\t%7s\n",
129 "#",
130 "FD",
131 "# Requests",
132 "Flags",
133 "Time",
134 "Offset");
135 for (link = hlp->servers.head; link; link = link->next) {
136 srv = link->data;
137 storeAppendPrintf(sentry, "%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\n",
138 srv->index + 1,
139 srv->rfd,
140 srv->stats.uses,
141 srv->flags.alive ? 'A' : ' ',
142 srv->flags.busy ? 'B' : ' ',
143 srv->flags.closing ? 'C' : ' ',
144 srv->flags.shutdown ? 'S' : ' ',
145 0.001 * tvSubMsec(srv->dispatch_time, current_time),
146 (int) srv->offset);
147 }
148 storeAppendPrintf(sentry, "\nFlags key:\n\n");
149 storeAppendPrintf(sentry, " A = ALIVE\n");
150 storeAppendPrintf(sentry, " B = BUSY\n");
151 storeAppendPrintf(sentry, " C = CLOSING\n");
152 storeAppendPrintf(sentry, " S = SHUTDOWN\n");
153}
154
155void
156helperShutdown(helper * hlp)
157{
c68e9c6b 158 dlink_node *link = hlp->servers.head;
74addf6c 159 helper_server *srv;
c68e9c6b 160 while (link) {
74addf6c 161 srv = link->data;
c68e9c6b 162 link = link->next;
74addf6c 163 if (!srv->flags.alive) {
164 debug(34, 3) ("helperShutdown: %s #%d is NOT ALIVE.\n",
165 hlp->id_name, srv->index + 1);
166 continue;
167 }
1f5f60dd 168 srv->flags.shutdown = 1; /* request it to shut itself down */
74addf6c 169 if (srv->flags.busy) {
170 debug(34, 3) ("helperShutdown: %s #%d is BUSY.\n",
171 hlp->id_name, srv->index + 1);
74addf6c 172 continue;
173 }
174 if (srv->flags.closing) {
175 debug(34, 3) ("helperShutdown: %s #%d is CLOSING.\n",
176 hlp->id_name, srv->index + 1);
177 continue;
178 }
74addf6c 179 srv->flags.closing = 1;
1f5f60dd 180 comm_close(srv->wfd);
74addf6c 181 }
182}
183
1f5f60dd 184helper *
185helperCreate(const char *name)
186{
c68e9c6b 187 helper *hlp = memAllocate(MEM_HELPER);
188 cbdataAdd(hlp, MEM_HELPER);
1f5f60dd 189 hlp->id_name = name;
190 return hlp;
191}
192
193void
194helperFree(helper * hlp)
195{
196 /* note, don't free hlp->name, it probably points to static memory */
fe73896c 197 if (hlp->queue.head)
198 debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n",
199 hlp->id_name, hlp->stats.queue_size);
1f5f60dd 200 cbdataFree(hlp);
201}
202
74addf6c 203/* ====================================================================== */
204/* LOCAL FUNCTIONS */
205/* ====================================================================== */
206
207static void
1f5f60dd 208helperServerFree(int fd, void *data)
74addf6c 209{
210 helper_server *srv = data;
211 helper *hlp = srv->parent;
212 assert(srv->rfd == fd);
213 if (srv->buf) {
214 memFree(MEM_8K_BUF, srv->buf);
215 srv->buf = NULL;
216 }
217 if (srv->wfd != srv->rfd)
218 comm_close(srv->wfd);
219 dlinkDelete(&srv->link, &hlp->servers);
74addf6c 220 hlp->n_running--;
221 assert(hlp->n_running >= 0);
1f5f60dd 222 if (!srv->flags.shutdown) {
223 debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
14e87a44 224 hlp->id_name, srv->index + 1, fd);
1f5f60dd 225 assert(hlp->n_running >= hlp->n_to_start / 2);
226 if (hlp->n_running < hlp->n_to_start / 2)
14e87a44 227 fatalf("Too few %s processes are running", hlp->id_name);
228 }
1f5f60dd 229 cbdataUnlock(srv->parent);
14e87a44 230 cbdataFree(srv);
74addf6c 231}
232
233static void
234helperHandleRead(int fd, void *data)
235{
236 int len;
237 char *t = NULL;
238 helper_server *srv = data;
239 helper_request *r;
240 helper *hlp = srv->parent;
241 assert(fd == srv->rfd);
242 assert(cbdataValid(data));
243 Counter.syscalls.sock.reads++;
244 len = read(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
245 fd_bytes(fd, len, FD_READ);
246 debug(29, 5) ("helperHandleRead: %d bytes from %s #%d.\n",
247 len, hlp->id_name, srv->index + 1);
248 if (len <= 0) {
249 if (len < 0)
250 debug(50, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror());
251 comm_close(fd);
252 return;
253 }
254 srv->offset += len;
255 srv->buf[srv->offset] = '\0';
256 r = srv->request;
257 if (r == NULL) {
258 /* someone spoke without being spoken to */
259 debug(29, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n",
260 hlp->id_name, srv->index + 1, len);
261 srv->offset = 0;
262 } else if ((t = strchr(srv->buf, '\n'))) {
263 /* end of reply found */
264 debug(29, 3) ("helperHandleRead: end of reply found\n");
265 *t = '\0';
266 if (cbdataValid(r->data))
267 r->callback(r->data, srv->buf);
268 srv->flags.busy = 0;
269 srv->offset = 0;
270 helperRequestFree(r);
271 hlp->stats.replies++;
272 hlp->stats.avg_svc_time =
273 intAverage(hlp->stats.avg_svc_time,
274 tvSubMsec(srv->dispatch_time, current_time),
275 hlp->stats.replies, REDIRECT_AV_FACTOR);
276 if (srv->flags.shutdown)
277 comm_close(srv->wfd);
c68e9c6b 278 else
279 helperKickQueue(hlp);
74addf6c 280 } else {
281 commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0);
282 }
74addf6c 283}
284
285static void
286Enqueue(helper * hlp, helper_request * r)
287{
c68e9c6b 288 dlink_node *link = memAllocate(MEM_DLINK_NODE);
74addf6c 289 dlinkAddTail(r, link, &hlp->queue);
290 hlp->stats.queue_size++;
291 if (hlp->stats.queue_size < hlp->n_running)
292 return;
293 if (squid_curtime - hlp->last_queue_warn < 600)
294 return;
fe73896c 295 if (shutting_down || reconfiguring)
296 return;
74addf6c 297 hlp->last_queue_warn = squid_curtime;
298 debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
299 debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
300 if (hlp->stats.queue_size > hlp->n_running * 2)
301 fatalf("Too many queued %s requests", hlp->id_name);
302 debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
303}
304
305static helper_request *
306Dequeue(helper * hlp)
307{
308 dlink_node *link;
309 helper_request *r = NULL;
310 if ((link = hlp->queue.head)) {
311 r = link->data;
312 dlinkDelete(link, &hlp->queue);
c68e9c6b 313 memFree(MEM_DLINK_NODE, link);
74addf6c 314 hlp->stats.queue_size--;
315 }
316 return r;
317}
318
319static helper_server *
320GetFirstAvailable(helper * hlp)
321{
322 dlink_node *n;
323 helper_server *srv = NULL;
fe73896c 324 if (hlp->n_running == 0)
325 return NULL;
74addf6c 326 for (n = hlp->servers.head; n != NULL; n = n->next) {
327 srv = n->data;
328 if (srv->flags.busy)
329 continue;
330 if (!srv->flags.alive)
331 continue;
332 return srv;
333 }
334 return NULL;
335}
336
337static void
338helperDispatch(helper_server * srv, helper_request * r)
339{
340 helper *hlp = srv->parent;
341 if (!cbdataValid(r->data)) {
342 debug(29, 1) ("helperDispatch: invalid callback data\n");
343 helperRequestFree(r);
344 return;
345 }
346 assert(!srv->flags.busy);
347 srv->flags.busy = 1;
348 srv->request = r;
349 srv->dispatch_time = current_time;
350 comm_write(srv->wfd,
351 r->buf,
352 strlen(r->buf),
353 NULL, /* Handler */
354 NULL, /* Handler-data */
355 NULL); /* free */
356 commSetSelect(srv->rfd,
357 COMM_SELECT_READ,
358 helperHandleRead,
359 srv, 0);
360 debug(29, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n",
361 hlp->id_name, srv->index + 1, strlen(r->buf));
362 srv->stats.uses++;
363 hlp->stats.requests++;
364}
365
366static void
367helperKickQueue(helper * hlp)
368{
369 helper_request *r;
370 helper_server *srv;
371 while ((srv = GetFirstAvailable(hlp)) && (r = Dequeue(hlp)))
372 helperDispatch(srv, r);
373}
374
375static void
376helperRequestFree(helper_request * r)
377{
378 cbdataUnlock(r->data);
379 xfree(r->buf);
c68e9c6b 380 memFree(MEM_HELPER_REQUEST, r);
74addf6c 381}