]> git.ipfire.org Git - thirdparty/squid.git/blame - src/helper.cc
Resolved debug section conflict between SSL and the cache store
[thirdparty/squid.git] / src / helper.cc
CommitLineData
f740a279 1
2/*
a15d85ea 3 * $Id: helper.cc,v 1.38 2002/05/15 19:08:34 wessels Exp $
f740a279 4 *
5 * DEBUG: section 29 Helper process maintenance
6 * AUTHOR: Harvest Derived?
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
f740a279 9 * ----------------------------------------------------------
10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
f740a279 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
74addf6c 36#include "squid.h"
37
38#define HELPER_MAX_ARGS 64
39
40static PF helperHandleRead;
94439e4e 41static PF helperStatefulHandleRead;
1f5f60dd 42static PF helperServerFree;
94439e4e 43static PF helperStatefulServerFree;
74addf6c 44static void Enqueue(helper * hlp, helper_request *);
45static helper_request *Dequeue(helper * hlp);
94439e4e 46static helper_stateful_request *StatefulDequeue(statefulhelper * hlp);
74addf6c 47static helper_server *GetFirstAvailable(helper * hlp);
94439e4e 48static helper_stateful_server *StatefulGetFirstAvailable(statefulhelper * hlp);
74addf6c 49static void helperDispatch(helper_server * srv, helper_request * r);
94439e4e 50static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r);
74addf6c 51static void helperKickQueue(helper * hlp);
94439e4e 52static void helperStatefulKickQueue(statefulhelper * hlp);
74addf6c 53static void helperRequestFree(helper_request * r);
94439e4e 54static void helperStatefulRequestFree(helper_stateful_request * r);
55static void StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r);
56static helper_stateful_request *StatefulServerDequeue(helper_stateful_server * srv);
57static void StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r);
58static void helperStatefulServerKickQueue(helper_stateful_server * srv);
74addf6c 59
60void
61helperOpenServers(helper * hlp)
62{
63 char *s;
64 char *progname;
65 char *shortname;
66 char *procname;
a2c963ae 67 const char *args[HELPER_MAX_ARGS];
74addf6c 68 char fd_note_buf[FD_DESC_SZ];
69 helper_server *srv;
70 int nargs = 0;
71 int k;
72 int x;
73 int rfd;
74 int wfd;
75 wordlist *w;
76 if (hlp->cmdline == NULL)
77 return;
78 progname = hlp->cmdline->key;
74addf6c 79 if ((s = strrchr(progname, '/')))
80 shortname = xstrdup(s + 1);
81 else
82 shortname = xstrdup(progname);
83 debug(29, 1) ("helperOpenServers: Starting %d '%s' processes\n",
84 hlp->n_to_start, shortname);
85 procname = xmalloc(strlen(shortname) + 3);
86 snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
87 args[nargs++] = procname;
88 for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
89 args[nargs++] = w->key;
90 args[nargs++] = NULL;
91 assert(nargs <= HELPER_MAX_ARGS);
92 for (k = 0; k < hlp->n_to_start; k++) {
c68e9c6b 93 getCurrentTime();
74addf6c 94 rfd = wfd = -1;
95 x = ipcCreate(hlp->ipc_type,
96 progname,
97 args,
98 shortname,
99 &rfd,
100 &wfd);
101 if (x < 0) {
102 debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
103 continue;
104 }
105 hlp->n_running++;
72711e31 106 srv = cbdataAlloc(helper_server);
74addf6c 107 srv->flags.alive = 1;
108 srv->index = k;
109 srv->rfd = rfd;
110 srv->wfd = wfd;
111 srv->buf = memAllocate(MEM_8K_BUF);
112 srv->buf_sz = 8192;
113 srv->offset = 0;
fa80a8ef 114 srv->parent = cbdataReference(hlp);
74addf6c 115 dlinkAddTail(srv, &srv->link, &hlp->servers);
116 if (rfd == wfd) {
117 snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
118 fd_note(rfd, fd_note_buf);
119 } else {
120 snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
121 fd_note(rfd, fd_note_buf);
122 snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
123 fd_note(wfd, fd_note_buf);
124 }
125 commSetNonBlocking(rfd);
126 if (wfd != rfd)
127 commSetNonBlocking(wfd);
1f5f60dd 128 comm_add_close_handler(rfd, helperServerFree, srv);
74addf6c 129 }
130 safe_free(shortname);
131 safe_free(procname);
838b993c 132 helperKickQueue(hlp);
74addf6c 133}
134
94439e4e 135void
136helperStatefulOpenServers(statefulhelper * hlp)
137{
138 char *s;
139 char *progname;
140 char *shortname;
141 char *procname;
a2c963ae 142 const char *args[HELPER_MAX_ARGS];
94439e4e 143 char fd_note_buf[FD_DESC_SZ];
144 helper_stateful_server *srv;
145 int nargs = 0;
146 int k;
147 int x;
148 int rfd;
149 int wfd;
150 wordlist *w;
151 if (hlp->cmdline == NULL)
152 return;
153 progname = hlp->cmdline->key;
154 if ((s = strrchr(progname, '/')))
155 shortname = xstrdup(s + 1);
156 else
157 shortname = xstrdup(progname);
158 debug(29, 1) ("helperStatefulOpenServers: Starting %d '%s' processes\n",
159 hlp->n_to_start, shortname);
160 procname = xmalloc(strlen(shortname) + 3);
161 snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
162 args[nargs++] = procname;
163 for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
164 args[nargs++] = w->key;
165 args[nargs++] = NULL;
166 assert(nargs <= HELPER_MAX_ARGS);
167 for (k = 0; k < hlp->n_to_start; k++) {
168 getCurrentTime();
169 rfd = wfd = -1;
170 x = ipcCreate(hlp->ipc_type,
171 progname,
172 args,
173 shortname,
174 &rfd,
175 &wfd);
176 if (x < 0) {
177 debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
178 continue;
179 }
180 hlp->n_running++;
72711e31 181 srv = cbdataAlloc(helper_stateful_server);
5d146f7d 182 srv->pid = x;
94439e4e 183 srv->flags.alive = 1;
184 srv->flags.reserved = S_HELPER_FREE;
185 srv->deferred_requests = 0;
60d096f4 186 srv->stats.deferbyfunc = 0;
187 srv->stats.deferbycb = 0;
188 srv->stats.submits = 0;
189 srv->stats.releases = 0;
94439e4e 190 srv->index = k;
191 srv->rfd = rfd;
192 srv->wfd = wfd;
193 srv->buf = memAllocate(MEM_8K_BUF);
194 srv->buf_sz = 8192;
195 srv->offset = 0;
fa80a8ef 196 srv->parent = cbdataReference(hlp);
94439e4e 197 if (hlp->datapool != NULL)
198 srv->data = memPoolAlloc(hlp->datapool);
94439e4e 199 dlinkAddTail(srv, &srv->link, &hlp->servers);
200 if (rfd == wfd) {
201 snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
202 fd_note(rfd, fd_note_buf);
203 } else {
204 snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
205 fd_note(rfd, fd_note_buf);
206 snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
207 fd_note(wfd, fd_note_buf);
208 }
209 commSetNonBlocking(rfd);
210 if (wfd != rfd)
211 commSetNonBlocking(wfd);
212 comm_add_close_handler(rfd, helperStatefulServerFree, srv);
213 }
214 safe_free(shortname);
215 safe_free(procname);
216 helperStatefulKickQueue(hlp);
217}
218
219
74addf6c 220void
221helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
222{
c68e9c6b 223 helper_request *r = memAllocate(MEM_HELPER_REQUEST);
74addf6c 224 helper_server *srv;
5b5f9257 225 if (hlp == NULL) {
7d47d8e6 226 debug(29, 3) ("helperSubmit: hlp == NULL\n");
5b5f9257 227 callback(data, NULL);
228 return;
229 }
74addf6c 230 r->callback = callback;
fa80a8ef 231 r->data = cbdataReference(data);
74addf6c 232 r->buf = xstrdup(buf);
74addf6c 233 if ((srv = GetFirstAvailable(hlp)))
234 helperDispatch(srv, r);
235 else
236 Enqueue(hlp, r);
94439e4e 237 debug(29, 9) ("helperSubmit: %s\n", buf);
238}
239
721b0310 240/* lastserver = "server last used as part of a deferred or reserved
241 * request sequence"
242 */
94439e4e 243void
244helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver)
245{
246 helper_stateful_request *r = memAllocate(MEM_HELPER_STATEFUL_REQUEST);
247 helper_stateful_server *srv;
248 if (hlp == NULL) {
249 debug(29, 3) ("helperStatefulSubmit: hlp == NULL\n");
250 callback(data, 0, NULL);
251 return;
252 }
253 r->callback = callback;
fa80a8ef 254 r->data = cbdataReference(data);
721b0310 255 if (buf != NULL) {
94439e4e 256 r->buf = xstrdup(buf);
721b0310 257 r->placeholder = 0;
258 } else {
259 r->buf = NULL;
94439e4e 260 r->placeholder = 1;
721b0310 261 }
94439e4e 262 if ((buf != NULL) && lastserver) {
38650cc8 263 debug(29, 5) ("StatefulSubmit with lastserver %p\n", lastserver);
60d096f4 264 /* the queue doesn't count for this assert because queued requests
265 * have already gone through here and been tested.
266 * It's legal to have deferred_requests == 0 and queue entries
267 * and status of S_HELPEER_DEFERRED.
268 * BUT: It's not legal to submit a new request w/lastserver in
269 * that state.
270 */
271 assert(!(lastserver->deferred_requests == 0 &&
272 lastserver->flags.reserved == S_HELPER_DEFERRED));
273 if (lastserver->flags.reserved != S_HELPER_RESERVED) {
274 lastserver->stats.submits++;
94439e4e 275 lastserver->deferred_requests--;
60d096f4 276 }
94439e4e 277 if (!(lastserver->request)) {
278 debug(29, 5) ("StatefulSubmit dispatching\n");
279 helperStatefulDispatch(lastserver, r);
280 } else {
281 debug(29, 5) ("StatefulSubmit queuing\n");
282 StatefulServerEnqueue(lastserver, r);
283 }
284 } else {
285 if ((srv = StatefulGetFirstAvailable(hlp))) {
286 helperStatefulDispatch(srv, r);
287 } else
288 StatefulEnqueue(hlp, r);
289 }
290 debug(29, 9) ("helperStatefulSubmit: placeholder: '%d', buf '%s'.\n", r->placeholder, buf);
291}
292
293helper_stateful_server *
294helperStatefulDefer(statefulhelper * hlp)
295/* find and add a deferred request to a server */
296{
297 dlink_node *n;
298 helper_stateful_server *srv = NULL, *rv = NULL;
299 if (hlp == NULL) {
300 debug(29, 3) ("helperStatefulReserve: hlp == NULL\n");
301 return NULL;
302 }
303 debug(29, 5) ("helperStatefulDefer: Running servers %d.\n", hlp->n_running);
304 if (hlp->n_running == 0) {
305 debug(29, 1) ("helperStatefulDefer: No running servers!. \n");
306 return NULL;
307 }
308 srv = StatefulGetFirstAvailable(hlp);
309 /* all currently busy:loop through servers and find server with the shortest queue */
310 rv = srv;
311 if (rv == NULL)
312 for (n = hlp->servers.head; n != NULL; n = n->next) {
313 srv = n->data;
314 if (srv->flags.reserved == S_HELPER_RESERVED)
315 continue;
316 if (!srv->flags.alive)
317 continue;
318 if ((hlp->IsAvailable != NULL) && (srv->data != NULL) &&
319 !(hlp->IsAvailable(srv->data)))
320 continue;
321 if ((rv != NULL) && (rv->deferred_requests < srv->deferred_requests))
322 continue;
323 rv = srv;
324 }
325 if (rv == NULL) {
326 debug(29, 1) ("helperStatefulDefer: None available.\n");
327 return NULL;
328 }
60d096f4 329 /* consistency check:
330 * when the deferred count is 0,
331 * submits + releases == deferbyfunc + deferbycb
332 * Or in english, when there are no deferred requests, the amount
333 * we have submitted to the queue or cancelled must equal the amount
334 * we have said we wanted to be able to submit or cancel
335 */
336 if (rv->deferred_requests == 0)
337 assert(rv->stats.submits + rv->stats.releases ==
338 rv->stats.deferbyfunc + rv->stats.deferbycb);
339
94439e4e 340 rv->flags.reserved = S_HELPER_DEFERRED;
341 rv->deferred_requests++;
60d096f4 342 rv->stats.deferbyfunc++;
94439e4e 343 return rv;
344}
345
346void
347helperStatefulReset(helper_stateful_server * srv)
348/* puts this helper back in the queue. the calling app is required to
349 * manage the state in the helper.
350 */
351{
352 statefulhelper *hlp = srv->parent;
353 helper_stateful_request *r;
354 r = srv->request;
355 if (r != NULL) {
356 /* reset attempt DURING an outstaning request */
357 debug(29, 1) ("helperStatefulReset: RESET During request %s \n",
358 hlp->id_name);
359 srv->flags.busy = 0;
360 srv->offset = 0;
361 helperStatefulRequestFree(r);
362 srv->request = NULL;
363 }
94439e4e 364 srv->flags.busy = 0;
365 if (srv->queue.head) {
366 srv->flags.reserved = S_HELPER_DEFERRED;
367 helperStatefulServerKickQueue(srv);
368 } else {
369 srv->flags.reserved = S_HELPER_FREE;
370 if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
371 srv->parent->OnEmptyQueue(srv->data);
372 helperStatefulKickQueue(hlp);
373 }
374}
375
376void
377helperStatefulReleaseServer(helper_stateful_server * srv)
378/*decrease the number of 'waiting' clients that set the helper to be DEFERRED */
379{
60d096f4 380 srv->stats.releases++;
381 if (srv->flags.reserved == S_HELPER_DEFERRED) {
382 assert(srv->deferred_requests);
94439e4e 383 srv->deferred_requests--;
60d096f4 384 }
94439e4e 385 if (!(srv->deferred_requests) && (srv->flags.reserved == S_HELPER_DEFERRED) && !(srv->queue.head)) {
386 srv->flags.reserved = S_HELPER_FREE;
387 if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
388 srv->parent->OnEmptyQueue(srv->data);
389 }
390}
391
392void *
393helperStatefulServerGetData(helper_stateful_server * srv)
394/* return a pointer to the stateful routines data area */
395{
396 return srv->data;
74addf6c 397}
398
399void
400helperStats(StoreEntry * sentry, helper * hlp)
401{
402 helper_server *srv;
403 dlink_node *link;
f4ae18d0 404 double tt;
74addf6c 405 storeAppendPrintf(sentry, "number running: %d of %d\n",
406 hlp->n_running, hlp->n_to_start);
407 storeAppendPrintf(sentry, "requests sent: %d\n",
408 hlp->stats.requests);
409 storeAppendPrintf(sentry, "replies received: %d\n",
410 hlp->stats.replies);
411 storeAppendPrintf(sentry, "queue length: %d\n",
412 hlp->stats.queue_size);
a15d85ea 413 storeAppendPrintf(sentry, "avg service time: %.2f msec\n",
414 (double) hlp->stats.avg_svc_time / 1000.0);
74addf6c 415 storeAppendPrintf(sentry, "\n");
592da4ec 416 storeAppendPrintf(sentry, "%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n",
74addf6c 417 "#",
418 "FD",
419 "# Requests",
420 "Flags",
421 "Time",
592da4ec 422 "Offset",
423 "Request");
74addf6c 424 for (link = hlp->servers.head; link; link = link->next) {
425 srv = link->data;
a15d85ea 426 tt = 0.001 * tvSubMsec(srv->dispatch_time,
427 srv->flags.busy ? current_time : srv->answer_time);
592da4ec 428 storeAppendPrintf(sentry, "%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\t%s\n",
74addf6c 429 srv->index + 1,
430 srv->rfd,
431 srv->stats.uses,
432 srv->flags.alive ? 'A' : ' ',
433 srv->flags.busy ? 'B' : ' ',
434 srv->flags.closing ? 'C' : ' ',
435 srv->flags.shutdown ? 'S' : ' ',
f4ae18d0 436 tt < 0.0 ? 0.0 : tt,
592da4ec 437 (int) srv->offset,
438 srv->request ? log_quote(srv->request->buf) : "(none)");
74addf6c 439 }
440 storeAppendPrintf(sentry, "\nFlags key:\n\n");
441 storeAppendPrintf(sentry, " A = ALIVE\n");
442 storeAppendPrintf(sentry, " B = BUSY\n");
443 storeAppendPrintf(sentry, " C = CLOSING\n");
444 storeAppendPrintf(sentry, " S = SHUTDOWN\n");
445}
446
94439e4e 447void
448helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp)
449{
450 helper_stateful_server *srv;
451 dlink_node *link;
452 double tt;
453 storeAppendPrintf(sentry, "number running: %d of %d\n",
454 hlp->n_running, hlp->n_to_start);
455 storeAppendPrintf(sentry, "requests sent: %d\n",
456 hlp->stats.requests);
457 storeAppendPrintf(sentry, "replies received: %d\n",
458 hlp->stats.replies);
459 storeAppendPrintf(sentry, "queue length: %d\n",
460 hlp->stats.queue_size);
461 storeAppendPrintf(sentry, "avg service time: %d msec\n",
462 hlp->stats.avg_svc_time);
463 storeAppendPrintf(sentry, "\n");
38650cc8 464 storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\t%7s\n",
94439e4e 465 "#",
466 "FD",
5d146f7d 467 "PID",
94439e4e 468 "# Requests",
469 "# Deferred Requests",
470 "Flags",
471 "Time",
472 "Offset",
473 "Request");
474 for (link = hlp->servers.head; link; link = link->next) {
475 srv = link->data;
476 tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time);
5d146f7d 477 storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%11d\t%c%c%c%c%c%c\t%7.3f\t%7d\t%s\n",
94439e4e 478 srv->index + 1,
479 srv->rfd,
5d146f7d 480 srv->pid,
94439e4e 481 srv->stats.uses,
32754419 482 (int) srv->deferred_requests,
94439e4e 483 srv->flags.alive ? 'A' : ' ',
484 srv->flags.busy ? 'B' : ' ',
485 srv->flags.closing ? 'C' : ' ',
486 srv->flags.reserved != S_HELPER_FREE ? 'R' : ' ',
487 srv->flags.shutdown ? 'S' : ' ',
5d146f7d 488 srv->request ? (srv->request->placeholder ? 'P' : ' ') : ' ',
94439e4e 489 tt < 0.0 ? 0.0 : tt,
490 (int) srv->offset,
491 srv->request ? log_quote(srv->request->buf) : "(none)");
492 }
493 storeAppendPrintf(sentry, "\nFlags key:\n\n");
494 storeAppendPrintf(sentry, " A = ALIVE\n");
495 storeAppendPrintf(sentry, " B = BUSY\n");
496 storeAppendPrintf(sentry, " C = CLOSING\n");
497 storeAppendPrintf(sentry, " R = RESERVED or DEFERRED\n");
498 storeAppendPrintf(sentry, " S = SHUTDOWN\n");
5d146f7d 499 storeAppendPrintf(sentry, " P = PLACEHOLDER\n");
94439e4e 500}
501
74addf6c 502void
503helperShutdown(helper * hlp)
504{
c68e9c6b 505 dlink_node *link = hlp->servers.head;
74addf6c 506 helper_server *srv;
c68e9c6b 507 while (link) {
74addf6c 508 srv = link->data;
c68e9c6b 509 link = link->next;
74addf6c 510 if (!srv->flags.alive) {
511 debug(34, 3) ("helperShutdown: %s #%d is NOT ALIVE.\n",
512 hlp->id_name, srv->index + 1);
513 continue;
514 }
1f5f60dd 515 srv->flags.shutdown = 1; /* request it to shut itself down */
74addf6c 516 if (srv->flags.busy) {
517 debug(34, 3) ("helperShutdown: %s #%d is BUSY.\n",
518 hlp->id_name, srv->index + 1);
74addf6c 519 continue;
520 }
521 if (srv->flags.closing) {
522 debug(34, 3) ("helperShutdown: %s #%d is CLOSING.\n",
523 hlp->id_name, srv->index + 1);
524 continue;
525 }
74addf6c 526 srv->flags.closing = 1;
3cdb7cd0 527 comm_close(srv->wfd);
528 srv->wfd = -1;
74addf6c 529 }
530}
531
94439e4e 532void
533helperStatefulShutdown(statefulhelper * hlp)
534{
535 dlink_node *link = hlp->servers.head;
536 helper_stateful_server *srv;
537 while (link) {
538 srv = link->data;
539 link = link->next;
540 if (!srv->flags.alive) {
541 debug(34, 3) ("helperStatefulShutdown: %s #%d is NOT ALIVE.\n",
542 hlp->id_name, srv->index + 1);
543 continue;
544 }
545 srv->flags.shutdown = 1; /* request it to shut itself down */
546 if (srv->flags.busy) {
547 debug(34, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n",
548 hlp->id_name, srv->index + 1);
549 continue;
550 }
551 if (srv->flags.closing) {
552 debug(34, 3) ("helperStatefulShutdown: %s #%d is CLOSING.\n",
553 hlp->id_name, srv->index + 1);
554 continue;
555 }
556 if (srv->flags.reserved != S_HELPER_FREE) {
557 debug(34, 3) ("helperStatefulShutdown: %s #%d is RESERVED.\n",
558 hlp->id_name, srv->index + 1);
559 continue;
560 }
561 if (srv->deferred_requests) {
562 debug(34, 3) ("helperStatefulShutdown: %s #%d has DEFERRED requests.\n",
563 hlp->id_name, srv->index + 1);
564 continue;
565 }
566 srv->flags.closing = 1;
567 comm_close(srv->wfd);
568 srv->wfd = -1;
569 }
570}
571
572
1f5f60dd 573helper *
574helperCreate(const char *name)
575{
28c60158 576 helper *hlp;
72711e31 577 hlp = cbdataAlloc(helper);
1f5f60dd 578 hlp->id_name = name;
579 return hlp;
580}
581
94439e4e 582statefulhelper *
583helperStatefulCreate(const char *name)
584{
585 statefulhelper *hlp;
72711e31 586 hlp = cbdataAlloc(statefulhelper);
94439e4e 587 hlp->id_name = name;
588 return hlp;
589}
590
591
1f5f60dd 592void
593helperFree(helper * hlp)
594{
5dae8514 595 if (!hlp)
596 return;
1f5f60dd 597 /* note, don't free hlp->name, it probably points to static memory */
fe73896c 598 if (hlp->queue.head)
599 debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n",
600 hlp->id_name, hlp->stats.queue_size);
1f5f60dd 601 cbdataFree(hlp);
602}
603
94439e4e 604void
605helperStatefulFree(statefulhelper * hlp)
606{
5dae8514 607 if (!hlp)
608 return;
94439e4e 609 /* note, don't free hlp->name, it probably points to static memory */
610 if (hlp->queue.head)
611 debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n",
612 hlp->id_name, hlp->stats.queue_size);
613 cbdataFree(hlp);
614}
615
616
74addf6c 617/* ====================================================================== */
618/* LOCAL FUNCTIONS */
619/* ====================================================================== */
620
621static void
1f5f60dd 622helperServerFree(int fd, void *data)
74addf6c 623{
624 helper_server *srv = data;
625 helper *hlp = srv->parent;
ac750329 626 helper_request *r;
74addf6c 627 assert(srv->rfd == fd);
628 if (srv->buf) {
db1cd23c 629 memFree(srv->buf, MEM_8K_BUF);
74addf6c 630 srv->buf = NULL;
631 }
ac750329 632 if ((r = srv->request)) {
fa80a8ef 633 void *cbdata;
634 if (cbdataReferenceValidDone(r->data, &cbdata))
635 r->callback(cbdata, srv->buf);
ac750329 636 helperRequestFree(r);
63758217 637 srv->request = NULL;
ac750329 638 }
3cdb7cd0 639 if (srv->wfd != srv->rfd && srv->wfd != -1)
74addf6c 640 comm_close(srv->wfd);
641 dlinkDelete(&srv->link, &hlp->servers);
74addf6c 642 hlp->n_running--;
643 assert(hlp->n_running >= 0);
1f5f60dd 644 if (!srv->flags.shutdown) {
645 debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
14e87a44 646 hlp->id_name, srv->index + 1, fd);
1f5f60dd 647 if (hlp->n_running < hlp->n_to_start / 2)
14e87a44 648 fatalf("Too few %s processes are running", hlp->id_name);
649 }
fa80a8ef 650 cbdataReferenceDone(srv->parent);
14e87a44 651 cbdataFree(srv);
74addf6c 652}
653
94439e4e 654static void
655helperStatefulServerFree(int fd, void *data)
656{
657 helper_stateful_server *srv = data;
658 statefulhelper *hlp = srv->parent;
659 helper_stateful_request *r;
660 assert(srv->rfd == fd);
661 if (srv->buf) {
662 memFree(srv->buf, MEM_8K_BUF);
663 srv->buf = NULL;
664 }
665 if ((r = srv->request)) {
fa80a8ef 666 void *cbdata;
667 if (cbdataReferenceValidDone(r->data, &cbdata))
668 r->callback(cbdata, srv, srv->buf);
94439e4e 669 helperStatefulRequestFree(r);
670 srv->request = NULL;
671 }
3c641669 672 /* TODO: walk the local queue of requests and carry them all out */
94439e4e 673 if (srv->wfd != srv->rfd && srv->wfd != -1)
674 comm_close(srv->wfd);
675 dlinkDelete(&srv->link, &hlp->servers);
676 hlp->n_running--;
677 assert(hlp->n_running >= 0);
678 if (!srv->flags.shutdown) {
679 debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
680 hlp->id_name, srv->index + 1, fd);
681 if (hlp->n_running < hlp->n_to_start / 2)
682 fatalf("Too few %s processes are running", hlp->id_name);
683 }
684 if (srv->data != NULL)
685 memPoolFree(hlp->datapool, srv->data);
fa80a8ef 686 cbdataReferenceDone(srv->parent);
94439e4e 687 cbdataFree(srv);
688}
689
690
74addf6c 691static void
692helperHandleRead(int fd, void *data)
693{
694 int len;
695 char *t = NULL;
696 helper_server *srv = data;
697 helper_request *r;
698 helper *hlp = srv->parent;
699 assert(fd == srv->rfd);
fa80a8ef 700 assert(cbdataReferenceValid(data));
83704487 701 statCounter.syscalls.sock.reads++;
1f7c9178 702 len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
74addf6c 703 fd_bytes(fd, len, FD_READ);
704 debug(29, 5) ("helperHandleRead: %d bytes from %s #%d.\n",
705 len, hlp->id_name, srv->index + 1);
706 if (len <= 0) {
707 if (len < 0)
708 debug(50, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror());
709 comm_close(fd);
710 return;
711 }
712 srv->offset += len;
713 srv->buf[srv->offset] = '\0';
714 r = srv->request;
715 if (r == NULL) {
716 /* someone spoke without being spoken to */
717 debug(29, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n",
718 hlp->id_name, srv->index + 1, len);
719 srv->offset = 0;
720 } else if ((t = strchr(srv->buf, '\n'))) {
721 /* end of reply found */
fa80a8ef 722 HLPCB *callback;
723 void *cbdata;
74addf6c 724 debug(29, 3) ("helperHandleRead: end of reply found\n");
725 *t = '\0';
fa80a8ef 726 callback = r->callback;
727 r->callback = NULL;
728 if (cbdataReferenceValidDone(r->data, &cbdata))
729 callback(cbdata, srv->buf);
74addf6c 730 srv->flags.busy = 0;
731 srv->offset = 0;
732 helperRequestFree(r);
63758217 733 srv->request = NULL;
74addf6c 734 hlp->stats.replies++;
a15d85ea 735 srv->answer_time = current_time;
74addf6c 736 hlp->stats.avg_svc_time =
737 intAverage(hlp->stats.avg_svc_time,
a15d85ea 738 tvSubUsec(srv->dispatch_time, current_time),
74addf6c 739 hlp->stats.replies, REDIRECT_AV_FACTOR);
3cdb7cd0 740 if (srv->flags.shutdown) {
74addf6c 741 comm_close(srv->wfd);
3cdb7cd0 742 srv->wfd = -1;
743 } else
c68e9c6b 744 helperKickQueue(hlp);
74addf6c 745 } else {
746 commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0);
747 }
74addf6c 748}
749
94439e4e 750static void
751helperStatefulHandleRead(int fd, void *data)
752{
753 int len;
754 char *t = NULL;
755 helper_stateful_server *srv = data;
756 helper_stateful_request *r;
757 statefulhelper *hlp = srv->parent;
758 assert(fd == srv->rfd);
fa80a8ef 759 assert(cbdataReferenceValid(data));
94439e4e 760 statCounter.syscalls.sock.reads++;
e7981f61 761 len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
94439e4e 762 fd_bytes(fd, len, FD_READ);
763 debug(29, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n",
764 len, hlp->id_name, srv->index + 1);
765 if (len <= 0) {
766 if (len < 0)
767 debug(50, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror());
768 comm_close(fd);
769 return;
770 }
771 srv->offset += len;
772 srv->buf[srv->offset] = '\0';
773 r = srv->request;
774 if (r == NULL) {
775 /* someone spoke without being spoken to */
776 debug(29, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n",
777 hlp->id_name, srv->index + 1, len);
778 srv->offset = 0;
779 } else if ((t = strchr(srv->buf, '\n'))) {
780 /* end of reply found */
781 debug(29, 3) ("helperStatefulHandleRead: end of reply found\n");
782 *t = '\0';
fa80a8ef 783 if (cbdataReferenceValid(r->data)) {
94439e4e 784 switch ((r->callback(r->data, srv, srv->buf))) { /*if non-zero reserve helper */
785 case S_HELPER_UNKNOWN:
786 fatal("helperStatefulHandleRead: either a non-state aware callback was give to the stateful helper routines, or an uninitialised callback response was recieved.\n");
787 break;
788 case S_HELPER_RELEASE: /* helper finished with */
60d096f4 789 if (!srv->deferred_requests && !srv->queue.head) {
94439e4e 790 srv->flags.reserved = S_HELPER_FREE;
791 if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
792 srv->parent->OnEmptyQueue(srv->data);
793 debug(29, 5) ("StatefulHandleRead: releasing %s #%d\n", hlp->id_name, srv->index + 1);
794 } else {
795 srv->flags.reserved = S_HELPER_DEFERRED;
796 debug(29, 5) ("StatefulHandleRead: outstanding deferred requests on %s #%d. reserving for deferred requests.\n", hlp->id_name, srv->index + 1);
797 }
798 break;
799 case S_HELPER_RESERVE: /* 'pin' this helper for the caller */
800 if (!srv->queue.head) {
60d096f4 801 assert(srv->deferred_requests == 0);
94439e4e 802 srv->flags.reserved = S_HELPER_RESERVED;
803 debug(29, 5) ("StatefulHandleRead: reserving %s #%d\n", hlp->id_name, srv->index + 1);
804 } else {
805 fatal("StatefulHandleRead: Callback routine attempted to reserve a stateful helper with deferred requests. This can lead to deadlock.\n");
806 }
807 break;
808 case S_HELPER_DEFER:
809 /* the helper is still needed, but can
810 * be used for other requests in the meantime.
811 */
812 srv->flags.reserved = S_HELPER_DEFERRED;
813 srv->deferred_requests++;
60d096f4 814 srv->stats.deferbycb++;
94439e4e 815 debug(29, 5) ("StatefulHandleRead: reserving %s #%d for deferred requests.\n", hlp->id_name, srv->index + 1);
816 break;
817 default:
818 fatal("helperStatefulHandleRead: unknown stateful helper callback result.\n");
819 }
820
821 } else {
822 debug(29, 1) ("StatefulHandleRead: no callback data registered\n");
823 }
824 srv->flags.busy = 0;
825 srv->offset = 0;
826 helperStatefulRequestFree(r);
827 srv->request = NULL;
828 hlp->stats.replies++;
829 hlp->stats.avg_svc_time =
830 intAverage(hlp->stats.avg_svc_time,
831 tvSubMsec(srv->dispatch_time, current_time),
832 hlp->stats.replies, REDIRECT_AV_FACTOR);
5d146f7d 833 if (srv->flags.shutdown
834 && srv->flags.reserved == S_HELPER_FREE
835 && !srv->deferred_requests) {
94439e4e 836 comm_close(srv->wfd);
837 srv->wfd = -1;
838 } else {
839 if (srv->queue.head)
840 helperStatefulServerKickQueue(srv);
841 else
842 helperStatefulKickQueue(hlp);
843 }
844 } else {
845 commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0);
846 }
847}
848
74addf6c 849static void
850Enqueue(helper * hlp, helper_request * r)
851{
c68e9c6b 852 dlink_node *link = memAllocate(MEM_DLINK_NODE);
74addf6c 853 dlinkAddTail(r, link, &hlp->queue);
854 hlp->stats.queue_size++;
855 if (hlp->stats.queue_size < hlp->n_running)
856 return;
857 if (squid_curtime - hlp->last_queue_warn < 600)
858 return;
fe73896c 859 if (shutting_down || reconfiguring)
860 return;
74addf6c 861 hlp->last_queue_warn = squid_curtime;
862 debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
863 debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
864 if (hlp->stats.queue_size > hlp->n_running * 2)
865 fatalf("Too many queued %s requests", hlp->id_name);
866 debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
867}
868
94439e4e 869static void
870StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r)
871{
872 dlink_node *link = memAllocate(MEM_DLINK_NODE);
873 dlinkAddTail(r, link, &hlp->queue);
874 hlp->stats.queue_size++;
875 if (hlp->stats.queue_size < hlp->n_running)
876 return;
893cbac6 877 if (hlp->stats.queue_size > hlp->n_running * 2)
878 fatalf("Too many queued %s requests", hlp->id_name);
94439e4e 879 if (squid_curtime - hlp->last_queue_warn < 600)
880 return;
881 if (shutting_down || reconfiguring)
882 return;
883 hlp->last_queue_warn = squid_curtime;
884 debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
885 debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
94439e4e 886 debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
887}
888
889static void
890StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r)
891{
892 dlink_node *link = memAllocate(MEM_DLINK_NODE);
893 dlinkAddTail(r, link, &srv->queue);
2d70df72 894/* TODO: warning if the queue on this server is more than X
895 * We don't check the queue size at the moment, because
896 * requests hitting here are deferrable
897 */
898/* hlp->stats.queue_size++;
899 * if (hlp->stats.queue_size < hlp->n_running)
900 * return;
901 * if (squid_curtime - hlp->last_queue_warn < 600)
902 * return;
903 * if (shutting_down || reconfiguring)
904 * return;
905 * hlp->last_queue_warn = squid_curtime;
906 * debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
907 * debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
908 * if (hlp->stats.queue_size > hlp->n_running * 2)
909 * fatalf("Too many queued %s requests", hlp->id_name);
910 * debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name); */
94439e4e 911}
912
913
74addf6c 914static helper_request *
915Dequeue(helper * hlp)
916{
917 dlink_node *link;
918 helper_request *r = NULL;
919 if ((link = hlp->queue.head)) {
920 r = link->data;
921 dlinkDelete(link, &hlp->queue);
db1cd23c 922 memFree(link, MEM_DLINK_NODE);
74addf6c 923 hlp->stats.queue_size--;
924 }
925 return r;
926}
927
94439e4e 928static helper_stateful_request *
929StatefulServerDequeue(helper_stateful_server * srv)
930{
931 dlink_node *link;
932 helper_stateful_request *r = NULL;
933 if ((link = srv->queue.head)) {
934 r = link->data;
935 dlinkDelete(link, &srv->queue);
936 memFree(link, MEM_DLINK_NODE);
937 }
938 return r;
939}
940
941static helper_stateful_request *
942StatefulDequeue(statefulhelper * hlp)
943{
944 dlink_node *link;
945 helper_stateful_request *r = NULL;
946 if ((link = hlp->queue.head)) {
947 r = link->data;
948 dlinkDelete(link, &hlp->queue);
949 memFree(link, MEM_DLINK_NODE);
950 hlp->stats.queue_size--;
951 }
952 return r;
953}
954
74addf6c 955static helper_server *
956GetFirstAvailable(helper * hlp)
957{
958 dlink_node *n;
959 helper_server *srv = NULL;
fe73896c 960 if (hlp->n_running == 0)
961 return NULL;
74addf6c 962 for (n = hlp->servers.head; n != NULL; n = n->next) {
963 srv = n->data;
964 if (srv->flags.busy)
965 continue;
966 if (!srv->flags.alive)
967 continue;
968 return srv;
969 }
970 return NULL;
971}
972
94439e4e 973static helper_stateful_server *
974StatefulGetFirstAvailable(statefulhelper * hlp)
975{
976 dlink_node *n;
977 helper_stateful_server *srv = NULL;
978 debug(29, 5) ("StatefulGetFirstAvailable: Running servers %d.\n", hlp->n_running);
979 if (hlp->n_running == 0)
980 return NULL;
981 for (n = hlp->servers.head; n != NULL; n = n->next) {
982 srv = n->data;
983 if (srv->flags.busy)
984 continue;
985 if (srv->flags.reserved == S_HELPER_RESERVED)
986 continue;
987 if (!srv->flags.alive)
988 continue;
989 if ((hlp->IsAvailable != NULL) && (srv->data != NULL) && !(hlp->IsAvailable(srv->data)))
990 continue;
991 return srv;
992 }
993 debug(29, 5) ("StatefulGetFirstAvailable: None available.\n");
994 return NULL;
995}
996
997
74addf6c 998static void
999helperDispatch(helper_server * srv, helper_request * r)
1000{
1001 helper *hlp = srv->parent;
fa80a8ef 1002 if (!cbdataReferenceValid(r->data)) {
74addf6c 1003 debug(29, 1) ("helperDispatch: invalid callback data\n");
1004 helperRequestFree(r);
1005 return;
1006 }
1007 assert(!srv->flags.busy);
1008 srv->flags.busy = 1;
1009 srv->request = r;
1010 srv->dispatch_time = current_time;
1011 comm_write(srv->wfd,
1012 r->buf,
1013 strlen(r->buf),
1014 NULL, /* Handler */
1015 NULL, /* Handler-data */
1016 NULL); /* free */
1017 commSetSelect(srv->rfd,
1018 COMM_SELECT_READ,
1019 helperHandleRead,
1020 srv, 0);
1021 debug(29, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n",
32754419 1022 hlp->id_name, srv->index + 1, (int) strlen(r->buf));
74addf6c 1023 srv->stats.uses++;
1024 hlp->stats.requests++;
1025}
1026
94439e4e 1027static void
1028helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r)
1029{
1030 statefulhelper *hlp = srv->parent;
fa80a8ef 1031 if (!cbdataReferenceValid(r->data)) {
94439e4e 1032 debug(29, 1) ("helperStatefulDispatch: invalid callback data\n");
1033 helperStatefulRequestFree(r);
1034 return;
1035 }
1036 debug(29, 9) ("helperStatefulDispatch busying helper %s #%d\n", hlp->id_name, srv->index + 1);
1037 if (r->placeholder == 1) {
1038 /* a callback is needed before this request can _use_ a helper. */
721b0310 1039 /* we don't care about releasing/deferring this helper. The request NEVER
1040 * gets to the helper. So we throw away the return code */
1041 r->callback(r->data, srv, NULL);
1042 /* throw away the placeholder */
1043 helperStatefulRequestFree(r);
1044 /* and push the queue. Note that the callback may have submitted a new
1045 * request to the helper which is why we test for the request*/
1046 if (srv->request == NULL) {
5d146f7d 1047 if (srv->flags.shutdown
1048 && srv->flags.reserved == S_HELPER_FREE
1049 && !srv->deferred_requests) {
721b0310 1050 comm_close(srv->wfd);
1051 srv->wfd = -1;
1052 } else {
1053 if (srv->queue.head)
1054 helperStatefulServerKickQueue(srv);
1055 else
1056 helperStatefulKickQueue(hlp);
94439e4e 1057 }
1058 }
1059 return;
1060 }
1061 srv->flags.busy = 1;
1062 srv->request = r;
1063 srv->dispatch_time = current_time;
1064 comm_write(srv->wfd,
1065 r->buf,
1066 strlen(r->buf),
1067 NULL, /* Handler */
1068 NULL, /* Handler-data */
1069 NULL); /* free */
1070 commSetSelect(srv->rfd,
1071 COMM_SELECT_READ,
1072 helperStatefulHandleRead,
1073 srv, 0);
1074 debug(29, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n",
32754419 1075 hlp->id_name, srv->index + 1, (int) strlen(r->buf));
94439e4e 1076 srv->stats.uses++;
1077 hlp->stats.requests++;
1078}
1079
1080
74addf6c 1081static void
1082helperKickQueue(helper * hlp)
1083{
1084 helper_request *r;
1085 helper_server *srv;
1086 while ((srv = GetFirstAvailable(hlp)) && (r = Dequeue(hlp)))
1087 helperDispatch(srv, r);
1088}
1089
94439e4e 1090static void
1091helperStatefulKickQueue(statefulhelper * hlp)
1092{
1093 helper_stateful_request *r;
1094 helper_stateful_server *srv;
1095 while ((srv = StatefulGetFirstAvailable(hlp)) && (r = StatefulDequeue(hlp)))
1096 helperStatefulDispatch(srv, r);
1097}
1098
1099static void
1100helperStatefulServerKickQueue(helper_stateful_server * srv)
1101{
1102 helper_stateful_request *r;
1103 if ((r = StatefulServerDequeue(srv)))
1104 helperStatefulDispatch(srv, r);
1105}
1106
74addf6c 1107static void
1108helperRequestFree(helper_request * r)
1109{
fa80a8ef 1110 cbdataReferenceDone(r->data);
74addf6c 1111 xfree(r->buf);
db1cd23c 1112 memFree(r, MEM_HELPER_REQUEST);
74addf6c 1113}
94439e4e 1114
1115static void
1116helperStatefulRequestFree(helper_stateful_request * r)
1117{
fa80a8ef 1118 cbdataReferenceDone(r->data);
94439e4e 1119 xfree(r->buf);
1120 memFree(r, MEM_HELPER_STATEFUL_REQUEST);
1121}