]>
Commit | Line | Data |
---|---|---|
74addf6c | 1 | #include "squid.h" |
2 | ||
3 | #define HELPER_MAX_ARGS 64 | |
4 | ||
5 | static PF helperHandleRead; | |
1f5f60dd | 6 | static PF helperServerFree; |
74addf6c | 7 | static void Enqueue(helper * hlp, helper_request *); |
8 | static helper_request *Dequeue(helper * hlp); | |
9 | static helper_server *GetFirstAvailable(helper * hlp); | |
10 | static void helperDispatch(helper_server * srv, helper_request * r); | |
11 | static void helperKickQueue(helper * hlp); | |
12 | static void helperRequestFree(helper_request * r); | |
13 | ||
14 | ||
15 | void | |
16 | helperOpenServers(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 | ||
92 | void | |
93 | helperSubmit(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 | ||
112 | void | |
113 | helperStats(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 | ||
155 | void | |
156 | helperShutdown(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 | 184 | helper * |
185 | helperCreate(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 | ||
193 | void | |
194 | helperFree(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 | ||
207 | static void | |
1f5f60dd | 208 | helperServerFree(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 | ||
233 | static void | |
234 | helperHandleRead(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 | ||
285 | static void | |
286 | Enqueue(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 | ||
305 | static helper_request * | |
306 | Dequeue(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 | ||
319 | static helper_server * | |
320 | GetFirstAvailable(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 | ||
337 | static void | |
338 | helperDispatch(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 | ||
366 | static void | |
367 | helperKickQueue(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 | ||
375 | static void | |
376 | helperRequestFree(helper_request * r) | |
377 | { | |
378 | cbdataUnlock(r->data); | |
379 | xfree(r->buf); | |
c68e9c6b | 380 | memFree(MEM_HELPER_REQUEST, r); |
74addf6c | 381 | } |