]> git.ipfire.org Git - thirdparty/squid.git/blame - src/helper.cc
s/MEM_ACL_PROXY_AUTH_DATA/MEM_ACL_USER_DATA/
[thirdparty/squid.git] / src / helper.cc
CommitLineData
f740a279 1
2/*
28c60158 3 * $Id: helper.cc,v 1.21 2001/01/05 09:51:38 adrian Exp $
f740a279 4 *
5 * DEBUG: section 29 Helper process maintenance
6 * AUTHOR: Harvest Derived?
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * CREDITS file for full details.
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;
1f5f60dd 41static PF helperServerFree;
74addf6c 42static void Enqueue(helper * hlp, helper_request *);
43static helper_request *Dequeue(helper * hlp);
44static helper_server *GetFirstAvailable(helper * hlp);
45static void helperDispatch(helper_server * srv, helper_request * r);
46static void helperKickQueue(helper * hlp);
47static void helperRequestFree(helper_request * r);
48
49
50void
51helperOpenServers(helper * hlp)
52{
53 char *s;
54 char *progname;
55 char *shortname;
56 char *procname;
57 char *args[HELPER_MAX_ARGS];
58 char fd_note_buf[FD_DESC_SZ];
59 helper_server *srv;
60 int nargs = 0;
61 int k;
62 int x;
63 int rfd;
64 int wfd;
65 wordlist *w;
66 if (hlp->cmdline == NULL)
67 return;
68 progname = hlp->cmdline->key;
74addf6c 69 if ((s = strrchr(progname, '/')))
70 shortname = xstrdup(s + 1);
71 else
72 shortname = xstrdup(progname);
73 debug(29, 1) ("helperOpenServers: Starting %d '%s' processes\n",
74 hlp->n_to_start, shortname);
75 procname = xmalloc(strlen(shortname) + 3);
76 snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
77 args[nargs++] = procname;
78 for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
79 args[nargs++] = w->key;
80 args[nargs++] = NULL;
81 assert(nargs <= HELPER_MAX_ARGS);
82 for (k = 0; k < hlp->n_to_start; k++) {
c68e9c6b 83 getCurrentTime();
74addf6c 84 rfd = wfd = -1;
85 x = ipcCreate(hlp->ipc_type,
86 progname,
87 args,
88 shortname,
89 &rfd,
90 &wfd);
91 if (x < 0) {
92 debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
93 continue;
94 }
95 hlp->n_running++;
28c60158 96 srv = CBDATA_ALLOC(helper_server, NULL);
74addf6c 97 srv->flags.alive = 1;
98 srv->index = k;
99 srv->rfd = rfd;
100 srv->wfd = wfd;
101 srv->buf = memAllocate(MEM_8K_BUF);
102 srv->buf_sz = 8192;
103 srv->offset = 0;
104 srv->parent = hlp;
1f5f60dd 105 cbdataLock(hlp); /* lock because of the parent backlink */
74addf6c 106 dlinkAddTail(srv, &srv->link, &hlp->servers);
107 if (rfd == wfd) {
108 snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
109 fd_note(rfd, fd_note_buf);
110 } else {
111 snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
112 fd_note(rfd, fd_note_buf);
113 snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
114 fd_note(wfd, fd_note_buf);
115 }
116 commSetNonBlocking(rfd);
117 if (wfd != rfd)
118 commSetNonBlocking(wfd);
1f5f60dd 119 comm_add_close_handler(rfd, helperServerFree, srv);
74addf6c 120 }
121 safe_free(shortname);
122 safe_free(procname);
838b993c 123 helperKickQueue(hlp);
74addf6c 124}
125
126void
127helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
128{
c68e9c6b 129 helper_request *r = memAllocate(MEM_HELPER_REQUEST);
74addf6c 130 helper_server *srv;
5b5f9257 131 if (hlp == NULL) {
7d47d8e6 132 debug(29, 3) ("helperSubmit: hlp == NULL\n");
5b5f9257 133 callback(data, NULL);
134 return;
135 }
74addf6c 136 r->callback = callback;
137 r->data = data;
138 r->buf = xstrdup(buf);
139 cbdataLock(r->data);
140 if ((srv = GetFirstAvailable(hlp)))
141 helperDispatch(srv, r);
142 else
143 Enqueue(hlp, r);
144}
145
146void
147helperStats(StoreEntry * sentry, helper * hlp)
148{
149 helper_server *srv;
150 dlink_node *link;
f4ae18d0 151 double tt;
74addf6c 152 storeAppendPrintf(sentry, "number running: %d of %d\n",
153 hlp->n_running, hlp->n_to_start);
154 storeAppendPrintf(sentry, "requests sent: %d\n",
155 hlp->stats.requests);
156 storeAppendPrintf(sentry, "replies received: %d\n",
157 hlp->stats.replies);
158 storeAppendPrintf(sentry, "queue length: %d\n",
159 hlp->stats.queue_size);
160 storeAppendPrintf(sentry, "avg service time: %d msec\n",
161 hlp->stats.avg_svc_time);
162 storeAppendPrintf(sentry, "\n");
592da4ec 163 storeAppendPrintf(sentry, "%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n",
74addf6c 164 "#",
165 "FD",
166 "# Requests",
167 "Flags",
168 "Time",
592da4ec 169 "Offset",
170 "Request");
74addf6c 171 for (link = hlp->servers.head; link; link = link->next) {
172 srv = link->data;
f4ae18d0 173 tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time);
592da4ec 174 storeAppendPrintf(sentry, "%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\t%s\n",
74addf6c 175 srv->index + 1,
176 srv->rfd,
177 srv->stats.uses,
178 srv->flags.alive ? 'A' : ' ',
179 srv->flags.busy ? 'B' : ' ',
180 srv->flags.closing ? 'C' : ' ',
181 srv->flags.shutdown ? 'S' : ' ',
f4ae18d0 182 tt < 0.0 ? 0.0 : tt,
592da4ec 183 (int) srv->offset,
184 srv->request ? log_quote(srv->request->buf) : "(none)");
74addf6c 185 }
186 storeAppendPrintf(sentry, "\nFlags key:\n\n");
187 storeAppendPrintf(sentry, " A = ALIVE\n");
188 storeAppendPrintf(sentry, " B = BUSY\n");
189 storeAppendPrintf(sentry, " C = CLOSING\n");
190 storeAppendPrintf(sentry, " S = SHUTDOWN\n");
191}
192
193void
194helperShutdown(helper * hlp)
195{
c68e9c6b 196 dlink_node *link = hlp->servers.head;
74addf6c 197 helper_server *srv;
c68e9c6b 198 while (link) {
74addf6c 199 srv = link->data;
c68e9c6b 200 link = link->next;
74addf6c 201 if (!srv->flags.alive) {
202 debug(34, 3) ("helperShutdown: %s #%d is NOT ALIVE.\n",
203 hlp->id_name, srv->index + 1);
204 continue;
205 }
1f5f60dd 206 srv->flags.shutdown = 1; /* request it to shut itself down */
74addf6c 207 if (srv->flags.busy) {
208 debug(34, 3) ("helperShutdown: %s #%d is BUSY.\n",
209 hlp->id_name, srv->index + 1);
74addf6c 210 continue;
211 }
212 if (srv->flags.closing) {
213 debug(34, 3) ("helperShutdown: %s #%d is CLOSING.\n",
214 hlp->id_name, srv->index + 1);
215 continue;
216 }
74addf6c 217 srv->flags.closing = 1;
3cdb7cd0 218 comm_close(srv->wfd);
219 srv->wfd = -1;
74addf6c 220 }
221}
222
1f5f60dd 223helper *
224helperCreate(const char *name)
225{
28c60158 226 helper *hlp;
227 hlp = CBDATA_ALLOC(helper, NULL);
1f5f60dd 228 hlp->id_name = name;
229 return hlp;
230}
231
232void
233helperFree(helper * hlp)
234{
235 /* note, don't free hlp->name, it probably points to static memory */
fe73896c 236 if (hlp->queue.head)
237 debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n",
238 hlp->id_name, hlp->stats.queue_size);
1f5f60dd 239 cbdataFree(hlp);
240}
241
74addf6c 242/* ====================================================================== */
243/* LOCAL FUNCTIONS */
244/* ====================================================================== */
245
246static void
1f5f60dd 247helperServerFree(int fd, void *data)
74addf6c 248{
249 helper_server *srv = data;
250 helper *hlp = srv->parent;
ac750329 251 helper_request *r;
74addf6c 252 assert(srv->rfd == fd);
253 if (srv->buf) {
db1cd23c 254 memFree(srv->buf, MEM_8K_BUF);
74addf6c 255 srv->buf = NULL;
256 }
ac750329 257 if ((r = srv->request)) {
258 if (cbdataValid(r->data))
259 r->callback(r->data, srv->buf);
260 helperRequestFree(r);
63758217 261 srv->request = NULL;
ac750329 262 }
3cdb7cd0 263 if (srv->wfd != srv->rfd && srv->wfd != -1)
74addf6c 264 comm_close(srv->wfd);
265 dlinkDelete(&srv->link, &hlp->servers);
74addf6c 266 hlp->n_running--;
267 assert(hlp->n_running >= 0);
1f5f60dd 268 if (!srv->flags.shutdown) {
269 debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
14e87a44 270 hlp->id_name, srv->index + 1, fd);
1f5f60dd 271 if (hlp->n_running < hlp->n_to_start / 2)
14e87a44 272 fatalf("Too few %s processes are running", hlp->id_name);
273 }
1f5f60dd 274 cbdataUnlock(srv->parent);
14e87a44 275 cbdataFree(srv);
74addf6c 276}
277
278static void
279helperHandleRead(int fd, void *data)
280{
281 int len;
282 char *t = NULL;
283 helper_server *srv = data;
284 helper_request *r;
285 helper *hlp = srv->parent;
286 assert(fd == srv->rfd);
287 assert(cbdataValid(data));
83704487 288 statCounter.syscalls.sock.reads++;
74addf6c 289 len = read(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
290 fd_bytes(fd, len, FD_READ);
291 debug(29, 5) ("helperHandleRead: %d bytes from %s #%d.\n",
292 len, hlp->id_name, srv->index + 1);
293 if (len <= 0) {
294 if (len < 0)
295 debug(50, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror());
296 comm_close(fd);
297 return;
298 }
299 srv->offset += len;
300 srv->buf[srv->offset] = '\0';
301 r = srv->request;
302 if (r == NULL) {
303 /* someone spoke without being spoken to */
304 debug(29, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n",
305 hlp->id_name, srv->index + 1, len);
306 srv->offset = 0;
307 } else if ((t = strchr(srv->buf, '\n'))) {
308 /* end of reply found */
309 debug(29, 3) ("helperHandleRead: end of reply found\n");
310 *t = '\0';
311 if (cbdataValid(r->data))
312 r->callback(r->data, srv->buf);
313 srv->flags.busy = 0;
314 srv->offset = 0;
315 helperRequestFree(r);
63758217 316 srv->request = NULL;
74addf6c 317 hlp->stats.replies++;
318 hlp->stats.avg_svc_time =
319 intAverage(hlp->stats.avg_svc_time,
320 tvSubMsec(srv->dispatch_time, current_time),
321 hlp->stats.replies, REDIRECT_AV_FACTOR);
3cdb7cd0 322 if (srv->flags.shutdown) {
74addf6c 323 comm_close(srv->wfd);
3cdb7cd0 324 srv->wfd = -1;
325 } else
c68e9c6b 326 helperKickQueue(hlp);
74addf6c 327 } else {
328 commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0);
329 }
74addf6c 330}
331
332static void
333Enqueue(helper * hlp, helper_request * r)
334{
c68e9c6b 335 dlink_node *link = memAllocate(MEM_DLINK_NODE);
74addf6c 336 dlinkAddTail(r, link, &hlp->queue);
337 hlp->stats.queue_size++;
338 if (hlp->stats.queue_size < hlp->n_running)
339 return;
340 if (squid_curtime - hlp->last_queue_warn < 600)
341 return;
fe73896c 342 if (shutting_down || reconfiguring)
343 return;
74addf6c 344 hlp->last_queue_warn = squid_curtime;
345 debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
346 debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
347 if (hlp->stats.queue_size > hlp->n_running * 2)
348 fatalf("Too many queued %s requests", hlp->id_name);
349 debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
350}
351
352static helper_request *
353Dequeue(helper * hlp)
354{
355 dlink_node *link;
356 helper_request *r = NULL;
357 if ((link = hlp->queue.head)) {
358 r = link->data;
359 dlinkDelete(link, &hlp->queue);
db1cd23c 360 memFree(link, MEM_DLINK_NODE);
74addf6c 361 hlp->stats.queue_size--;
362 }
363 return r;
364}
365
366static helper_server *
367GetFirstAvailable(helper * hlp)
368{
369 dlink_node *n;
370 helper_server *srv = NULL;
fe73896c 371 if (hlp->n_running == 0)
372 return NULL;
74addf6c 373 for (n = hlp->servers.head; n != NULL; n = n->next) {
374 srv = n->data;
375 if (srv->flags.busy)
376 continue;
377 if (!srv->flags.alive)
378 continue;
379 return srv;
380 }
381 return NULL;
382}
383
384static void
385helperDispatch(helper_server * srv, helper_request * r)
386{
387 helper *hlp = srv->parent;
388 if (!cbdataValid(r->data)) {
389 debug(29, 1) ("helperDispatch: invalid callback data\n");
390 helperRequestFree(r);
391 return;
392 }
393 assert(!srv->flags.busy);
394 srv->flags.busy = 1;
395 srv->request = r;
396 srv->dispatch_time = current_time;
397 comm_write(srv->wfd,
398 r->buf,
399 strlen(r->buf),
400 NULL, /* Handler */
401 NULL, /* Handler-data */
402 NULL); /* free */
403 commSetSelect(srv->rfd,
404 COMM_SELECT_READ,
405 helperHandleRead,
406 srv, 0);
407 debug(29, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n",
408 hlp->id_name, srv->index + 1, strlen(r->buf));
409 srv->stats.uses++;
410 hlp->stats.requests++;
411}
412
413static void
414helperKickQueue(helper * hlp)
415{
416 helper_request *r;
417 helper_server *srv;
418 while ((srv = GetFirstAvailable(hlp)) && (r = Dequeue(hlp)))
419 helperDispatch(srv, r);
420}
421
422static void
423helperRequestFree(helper_request * r)
424{
425 cbdataUnlock(r->data);
426 xfree(r->buf);
db1cd23c 427 memFree(r, MEM_HELPER_REQUEST);
74addf6c 428}