--------------------- PatchSet 6126 Date: 2004/01/18 01:09:40 Author: hno Branch: cerberian Tag: (none) Log: Import of the external acl additions required by the Cerberian URL filter Members: src/HttpRequest.c:1.7->1.7.80.1 src/cf.data.pre:1.49.2.51->1.49.2.51.2.1 src/errorpage.c:1.15.6.9->1.15.6.9.2.1 src/external_acl.c:1.2.4.23->1.2.4.23.2.1 src/globals.h:1.14.6.3->1.14.6.3.10.1 src/helper.c:1.16.2.10->1.16.2.10.4.1 src/protos.h:1.41.6.17->1.41.6.17.2.1 src/redirect.c:1.7.52.3->1.7.52.3.2.1 src/structs.h:1.48.2.20->1.48.2.20.2.1 Index: squid/src/HttpRequest.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/HttpRequest.c,v retrieving revision 1.7 retrieving revision 1.7.80.1 diff -u -r1.7 -r1.7.80.1 --- squid/src/HttpRequest.c 14 Apr 2001 00:31:01 -0000 1.7 +++ squid/src/HttpRequest.c 18 Jan 2004 01:09:40 -0000 1.7.80.1 @@ -1,6 +1,6 @@ /* - * $Id: HttpRequest.c,v 1.7 2001/04/14 00:31:01 squidadm Exp $ + * $Id: HttpRequest.c,v 1.7.80.1 2004/01/18 01:09:40 hno Exp $ * * DEBUG: section 73 HTTP Request * AUTHOR: Duane Wessels @@ -67,6 +67,7 @@ httpHdrCcDestroy(req->cache_control); if (req->range) httpHdrRangeDestroy(req->range); + stringClean(&req->extacl_log); memFree(req, MEM_REQUEST_T); } Index: squid/src/cf.data.pre =================================================================== RCS file: /cvsroot/squid-sf//squid/src/cf.data.pre,v retrieving revision 1.49.2.51 retrieving revision 1.49.2.51.2.1 diff -u -r1.49.2.51 -r1.49.2.51.2.1 --- squid/src/cf.data.pre 20 Dec 2003 03:13:43 -0000 1.49.2.51 +++ squid/src/cf.data.pre 18 Jan 2004 01:09:40 -0000 1.49.2.51.2.1 @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.49.2.51 2003/12/20 03:13:43 squidadm Exp $ +# $Id: cf.data.pre,v 1.49.2.51.2.1 2004/01/18 01:09:40 hno Exp $ # # # SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -1221,6 +1221,16 @@ and other system resources. DOC_END +NAME: redirect_concurrency +TYPE: int +DEFAULT: 0 +LOC: Config.redirectConcurrency +DOC_START + The number of requests each redirector helper can handle in + parallell. Defaults to 0 which indicates that the redirector + is a old-style singlethreaded redirector. +DOC_END + NAME: redirect_rewrites_host_header TYPE: onoff DEFAULT: on @@ -1494,9 +1504,11 @@ negative_ttl=n TTL for cached negative lookups (default same as ttl) - children=n Concurrency level / number of processes spawn - to service external acl lookups of this type. + children=n number of processes spawn to service external acl + lookups of this type. Note: see compatibility note below + concurrency=n concurrency level per process. Use 0 for simple helpers + who can only process a single request at a time. cache=n result cache size, 0 is unbounded (default) FORMAT specifications @@ -1530,8 +1542,11 @@ Defined keywords: - user= The users name (login) - error= Error description (only defined for ERR results) + user= The users name (login also understood) + message= Error message or similar used as %o in error messages + (error also understood) + log= String to be logged in access.log. Available as + %ea in logformat specifications Keyword values need to be enclosed in quotes if they may contain whitespace, or the whitespace escaped using \. Any quotes or \ Index: squid/src/errorpage.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/errorpage.c,v retrieving revision 1.15.6.9 retrieving revision 1.15.6.9.2.1 diff -u -r1.15.6.9 -r1.15.6.9.2.1 --- squid/src/errorpage.c 7 Nov 2003 03:14:30 -0000 1.15.6.9 +++ squid/src/errorpage.c 18 Jan 2004 01:09:40 -0000 1.15.6.9.2.1 @@ -1,6 +1,6 @@ /* - * $Id: errorpage.c,v 1.15.6.9 2003/11/07 03:14:30 squidadm Exp $ + * $Id: errorpage.c,v 1.15.6.9.2.1 2004/01/18 01:09:40 hno Exp $ * * DEBUG: section 4 Error Generation * AUTHOR: Duane Wessels @@ -426,6 +426,7 @@ * L - HREF link for more info/contact x * M - Request Method x * m - Error message returned by external Auth. x + * o - Error message returned by external ACL x * p - URL port # x * P - Protocol x * R - Full HTTP Request x @@ -510,6 +511,11 @@ case 'M': p = r ? RequestMethodStr[r->method] : "[unkown method]"; break; + case 'o': + p = external_acl_message; + if (!p) + p = "[not available]"; + break; case 'p': if (r) { memBufPrintf(&mb, "%d", (int) r->port); Index: squid/src/external_acl.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/external_acl.c,v retrieving revision 1.2.4.23 retrieving revision 1.2.4.23.2.1 diff -u -r1.2.4.23 -r1.2.4.23.2.1 --- squid/src/external_acl.c 20 Nov 2003 03:14:13 -0000 1.2.4.23 +++ squid/src/external_acl.c 18 Jan 2004 01:09:40 -0000 1.2.4.23.2.1 @@ -1,6 +1,6 @@ /* - * $Id: external_acl.c,v 1.2.4.23 2003/11/20 03:14:13 squidadm Exp $ + * $Id: external_acl.c,v 1.2.4.23.2.1 2004/01/18 01:09:40 hno Exp $ * * DEBUG: section 82 External ACL * AUTHOR: Henrik Nordstrom, MARA Systems AB @@ -45,8 +45,8 @@ #ifndef DEFAULT_EXTERNAL_ACL_TTL #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60 #endif -#ifndef DEFAULT_EXTERNAL_ACL_CONCURRENCY -#define DEFAULT_EXTERNAL_ACL_CONCURRENCY 5 +#ifndef DEFAULT_EXTERNAL_ACL_CHILDREN +#define DEFAULT_EXTERNAL_ACL_CHILDREN 5 #endif typedef struct _external_acl_format external_acl_format; @@ -67,7 +67,8 @@ int result; time_t date; char *user; - char *error; + char *message; + char *log; external_acl *def; }; @@ -82,6 +83,7 @@ external_acl_format *format; wordlist *cmdline; int children; + int concurrency; helper *helper; hash_table *cache; dlink_list lru_list; @@ -101,6 +103,7 @@ EXT_ACL_DST, EXT_ACL_PROTO, EXT_ACL_PORT, + EXT_ACL_PATH, EXT_ACL_METHOD, EXT_ACL_HEADER, EXT_ACL_HEADER_MEMBER, @@ -163,7 +166,7 @@ a->ttl = DEFAULT_EXTERNAL_ACL_TTL; a->negative_ttl = -1; - a->children = DEFAULT_EXTERNAL_ACL_CONCURRENCY; + a->children = DEFAULT_EXTERNAL_ACL_CHILDREN; token = strtok(NULL, w_space); if (!token) @@ -180,7 +183,7 @@ } else if (strncmp(token, "children=", 9) == 0) { a->children = atoi(token + 9); } else if (strncmp(token, "concurrency=", 12) == 0) { - a->children = atoi(token + 12); + a->concurrency = atoi(token + 12); } else if (strncmp(token, "cache=", 6) == 0) { a->cache_size = atoi(token + 6); } else { @@ -251,6 +254,8 @@ format->type = EXT_ACL_PROTO; else if (strcmp(token, "%PORT") == 0) format->type = EXT_ACL_PORT; + else if (strcmp(token, "%PATH") == 0) + format->type = EXT_ACL_PATH; else if (strcmp(token, "%METHOD") == 0) format->type = EXT_ACL_METHOD; else { @@ -290,8 +295,10 @@ storeAppendPrintf(sentry, " ttl=%d", node->ttl); if (node->negative_ttl != node->ttl) storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl); - if (node->children != DEFAULT_EXTERNAL_ACL_CONCURRENCY) - storeAppendPrintf(sentry, " concurrency=%d", node->children); + if (node->children != DEFAULT_EXTERNAL_ACL_CHILDREN) + storeAppendPrintf(sentry, " children=%d", node->children); + if (node->concurrency) + storeAppendPrintf(sentry, " concurrency=%d", node->concurrency); for (format = node->format; format; format = format->next) { switch (format->type) { case EXT_ACL_HEADER: @@ -314,6 +321,7 @@ DUMP_EXT_ACL_TYPE(DST); DUMP_EXT_ACL_TYPE(PROTO); DUMP_EXT_ACL_TYPE(PORT); + DUMP_EXT_ACL_TYPE(PATH); DUMP_EXT_ACL_TYPE(METHOD); } } @@ -451,6 +459,8 @@ } external_acl_cache_touch(acl->def, entry); result = entry->result; + external_acl_message = entry->message; + debug(82, 2) ("aclMatchExternal: %s = %d\n", acl->def->name, result); /* FIXME: This should allocate it's own storage in the request. This * piggy backs on ident, and may fail if there is child proxies.. @@ -461,6 +471,8 @@ if (cbdataValid(ch->conn)) xstrncpy(ch->conn->rfc931, entry->user, USER_IDENT_SZ); } + if (entry->log) + stringReset(&ch->request->extacl_log, entry->log); return result; } @@ -533,6 +545,9 @@ snprintf(buf, sizeof(buf), "%d", request->port); str = buf; break; + case EXT_ACL_PATH: + str = strBuf(request->urlpath); + break; case EXT_ACL_METHOD: str = RequestMethodStr[request->method]; break; @@ -588,11 +603,12 @@ external_acl_entry *entry = data; safe_free(entry->hash.key); safe_free(entry->user); - safe_free(entry->error); + safe_free(entry->message); + safe_free(entry->log); } static external_acl_entry * -external_acl_cache_add(external_acl * def, const char *key, int result, char *user, char *error) +external_acl_cache_add(external_acl * def, const char *key, int result, char *user, char *message, char *log) { external_acl_entry *entry = hash_lookup(def->cache, key); debug(82, 2) ("external_acl_cache_add: Adding '%s' = %d\n", key, result); @@ -601,11 +617,13 @@ entry->date = squid_curtime; entry->result = result; safe_free(entry->user); - safe_free(entry->error); + safe_free(entry->message); if (user) entry->user = xstrdup(user); - if (error) - entry->error = xstrdup(error); + if (message) + entry->message = xstrdup(message); + if (log) + entry->log = xstrdup(log); external_acl_cache_touch(def, entry); return entry; } @@ -619,8 +637,8 @@ entry->result = result; if (user) entry->user = xstrdup(user); - if (error) - entry->error = xstrdup(error); + if (message) + entry->message = xstrdup(message); entry->def = def; hash_join(def->cache, &entry->hash); dlinkAdd(entry, &entry->lru, &def->lru_list); @@ -663,11 +681,7 @@ /* * The helper program receives queries on stdin, one - * per line, and must return the result on on stdout as - * OK user="Users login name" - * on success, and - * ERR error="Description of the error" - * on error (the user/error options are optional) + * per line, and must return the result on on stdout * * General result syntax: * @@ -676,7 +690,8 @@ * Keywords: * * user= The users name (login) - * error= Error description (only defined for ERR results) + * message= Message describing the reason + * log= A string to be used in access logging * * Other keywords may be added to the protocol later * @@ -696,7 +711,8 @@ char *value; char *t; char *user = NULL; - char *error = NULL; + char *message = NULL; + char *log = NULL; external_acl_entry *entry = NULL; debug(82, 2) ("externalAclHandleReply: reply=\"%s\"\n", reply); @@ -712,15 +728,21 @@ *value++ = '\0'; /* terminate the token, and move up to the value */ if (strcmp(token, "user") == 0) user = value; + else if (strcmp(token, "login") == 0) + user = value; else if (strcmp(token, "error") == 0) - error = value; + message = value; + else if (strcmp(token, "message") == 0) + message = value; + else if (strcmp(token, "log") == 0) + log = value; } } } dlinkDelete(&state->list, &state->def->queue); if (cbdataValid(state->def)) { if (reply) - entry = external_acl_cache_add(state->def, state->key, result, user, error); + entry = external_acl_cache_add(state->def, state->key, result, user, message, log); else { external_acl_entry *oldentry = hash_lookup(state->def->cache, state->key); if (oldentry) @@ -731,6 +753,11 @@ cbdataUnlock(state->def); state->def = NULL; + if (entry) + external_acl_message = entry->message; + else + external_acl_message = NULL; + if (cbdataValid(state->callback_data)) state->callback(state->callback_data, entry); cbdataUnlock(state->callback_data); @@ -742,6 +769,12 @@ } while (state); } +const char * +externalAclMessage(external_acl_entry * entry) +{ + return entry->message; +} + void externalAclLookup(aclCheck_t * ch, void *acl_data, EAH * callback, void *callback_data) { @@ -764,6 +797,7 @@ ch->auth_user_request = NULL; if (!key) { debug(82, 1) ("externalAclLookup: lookup in '%s', prerequisit failure\n", def->name); + external_acl_message = "MISSING REQUIRED INFORMATION"; callback(callback_data, NULL); return; } @@ -791,6 +825,7 @@ } else { /* There is a cached valid result.. use it */ /* This should not really happen, but what the heck.. */ + external_acl_message = entry->message; callback(callback_data, entry); cbdataFree(state); return; @@ -798,11 +833,10 @@ } /* Check for queue overload */ if (def->helper->stats.queue_size >= def->helper->n_running) { - int result = -1; external_acl_entry *entry = hash_lookup(def->cache, key); debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def->name); if (entry) - result = entry->result; + external_acl_message = entry->message; cbdataFree(state); callback(callback_data, entry); return; @@ -811,7 +845,7 @@ memBufDefInit(&buf); memBufPrintf(&buf, "%s\n", key); helperSubmit(def->helper, buf.buf, externalAclHandleReply, state); - external_acl_cache_add(def, key, -1, NULL, NULL); + external_acl_cache_add(def, key, -1, NULL, NULL, NULL); dlinkAdd(state, &state->list, &def->queue); memBufClean(&buf); } @@ -842,6 +876,7 @@ p->helper = helperCreate(p->name); p->helper->cmdline = p->cmdline; p->helper->n_to_start = p->children; + p->helper->concurrency = p->concurrency; p->helper->ipc_type = IPC_TCP_SOCKET; helperOpenServers(p->helper); } Index: squid/src/globals.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/globals.h,v retrieving revision 1.14.6.3 retrieving revision 1.14.6.3.10.1 diff -u -r1.14.6.3 -r1.14.6.3.10.1 --- squid/src/globals.h 14 Jan 2003 03:14:58 -0000 1.14.6.3 +++ squid/src/globals.h 18 Jan 2004 01:09:40 -0000 1.14.6.3.10.1 @@ -1,6 +1,6 @@ /* - * $Id: globals.h,v 1.14.6.3 2003/01/14 03:14:58 squidadm Exp $ + * $Id: globals.h,v 1.14.6.3.10.1 2004/01/18 01:09:40 hno Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -163,6 +163,7 @@ extern unsigned int WIN32_OS_version; /* 0 */ extern char *WIN32_OS_string; /* NULL */ #endif +extern const char *external_acl_message; /* NULL */ #if HAVE_SBRK extern void *sbrk_start; /* 0 */ #endif Index: squid/src/helper.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/helper.c,v retrieving revision 1.16.2.10 retrieving revision 1.16.2.10.4.1 diff -u -r1.16.2.10 -r1.16.2.10.4.1 --- squid/src/helper.c 13 Sep 2003 02:14:23 -0000 1.16.2.10 +++ squid/src/helper.c 18 Jan 2004 01:09:40 -0000 1.16.2.10.4.1 @@ -1,6 +1,6 @@ /* - * $Id: helper.c,v 1.16.2.10 2003/09/13 02:14:23 squidadm Exp $ + * $Id: helper.c,v 1.16.2.10.4.1 2004/01/18 01:09:40 hno Exp $ * * DEBUG: section 84 Helper process maintenance * AUTHOR: Harvest Derived? @@ -109,9 +109,11 @@ srv->index = k; srv->rfd = rfd; srv->wfd = wfd; - srv->buf = memAllocate(MEM_8K_BUF); - srv->buf_sz = 8192; - srv->offset = 0; + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + srv->rbuf = memAllocate(MEM_8K_BUF); + srv->rbuf_sz = 8192; + srv->roffset = 0; + srv->requests = xcalloc(hlp->concurrency ? hlp->concurrency : 1, sizeof(*srv->requests)); srv->parent = hlp; cbdataLock(hlp); /* lock because of the parent backlink */ dlinkAddTail(srv, &srv->link, &hlp->servers); @@ -128,6 +130,10 @@ if (wfd != rfd) commSetNonBlocking(wfd); comm_add_close_handler(rfd, helperServerFree, srv); + commSetSelect(srv->rfd, + COMM_SELECT_READ, + helperHandleRead, + srv, 0); } hlp->last_restart = squid_curtime; safe_free(shortname); @@ -193,9 +199,11 @@ srv->index = k; srv->rfd = rfd; srv->wfd = wfd; - srv->buf = memAllocate(MEM_8K_BUF); - srv->buf_sz = 8192; - srv->offset = 0; + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + srv->rbuf = memAllocate(MEM_8K_BUF); + srv->rbuf_sz = 8192; + srv->roffset = 0; + srv->requests = xcalloc(hlp->concurrency ? hlp->concurrency : 1, sizeof(*srv->requests)); srv->parent = hlp; if (hlp->datapool != NULL) srv->data = memPoolAlloc(hlp->datapool); @@ -214,6 +222,10 @@ if (wfd != rfd) commSetNonBlocking(wfd); comm_add_close_handler(rfd, helperStatefulServerFree, srv); + commSetSelect(srv->rfd, + COMM_SELECT_READ, + helperStatefulHandleRead, + srv, 0); } hlp->last_restart = squid_curtime; safe_free(shortname); @@ -281,7 +293,8 @@ lastserver->stats.submits++; lastserver->deferred_requests--; } - if (!(lastserver->request)) { + /* XXX This needs to remember last slot */ + if (!(lastserver->stats.pending)) { debug(84, 5) ("StatefulSubmit dispatching\n"); helperStatefulDispatch(lastserver, r); } else { @@ -361,17 +374,16 @@ { statefulhelper *hlp = srv->parent; helper_stateful_request *r; - r = srv->request; + int slot = 0; /* XXX Needs to know which slot to reset */ + r = srv->requests[slot]; if (r != NULL) { /* reset attempt DURING an outstaning request */ debug(84, 1) ("helperStatefulReset: RESET During request %s \n", hlp->id_name); - srv->flags.busy = 0; - srv->offset = 0; + srv->stats.pending--; + srv->requests[slot] = NULL; helperStatefulRequestFree(r); - srv->request = NULL; } - srv->flags.busy = 0; if (srv->queue.head) { srv->flags.reserved = S_HELPER_DEFERRED; helperStatefulServerKickQueue(srv); @@ -409,9 +421,7 @@ void helperStats(StoreEntry * sentry, helper * hlp) { - helper_server *srv; dlink_node *link; - double tt; storeAppendPrintf(sentry, "program: %s\n", hlp->cmdline->key); storeAppendPrintf(sentry, "number running: %d of %d\n", @@ -425,31 +435,33 @@ storeAppendPrintf(sentry, "avg service time: %.2f msec\n", (double) hlp->stats.avg_svc_time / 1000.0); storeAppendPrintf(sentry, "\n"); - storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n", + storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%9s\t%s\t%7s\t%7s\t%7s\n", "#", "FD", "PID", "# Requests", + "# Pending", "Flags", "Time", "Offset", "Request"); for (link = hlp->servers.head; link; link = link->next) { - srv = link->data; - tt = 0.001 * tvSubMsec(srv->dispatch_time, - srv->flags.busy ? current_time : srv->answer_time); - storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%c%c%c%c\t%7.3f\t%7d\t%s\n", + helper_server *srv = link->data; + double tt = srv->requests[0] ? 0.001 * + tvSubMsec(srv->requests[0]->dispatch_time, current_time) : 0.0; + storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%9d\t%c%c%c%c\t%7.3f\t%7d\t%s\n", srv->index + 1, srv->rfd, srv->pid, srv->stats.uses, + srv->stats.pending, srv->flags.alive ? 'A' : ' ', - srv->flags.busy ? 'B' : ' ', + srv->stats.pending ? 'B' : ' ', srv->flags.closing ? 'C' : ' ', srv->flags.shutdown ? 'S' : ' ', tt < 0.0 ? 0.0 : tt, - (int) srv->offset, - srv->request ? log_quote(srv->request->buf) : "(none)"); + (int) srv->roffset, + srv->requests[0] ? log_quote(srv->requests[0]->buf) : "(none)"); } storeAppendPrintf(sentry, "\nFlags key:\n\n"); storeAppendPrintf(sentry, " A = ALIVE\n"); @@ -461,9 +473,7 @@ void helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp) { - helper_stateful_server *srv; dlink_node *link; - double tt; storeAppendPrintf(sentry, "number running: %d of %d\n", hlp->n_running, hlp->n_to_start); storeAppendPrintf(sentry, "requests sent: %d\n", @@ -475,34 +485,37 @@ storeAppendPrintf(sentry, "avg service time: %d msec\n", hlp->stats.avg_svc_time); storeAppendPrintf(sentry, "\n"); - storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%20s\t%s\t%7s\t%7s\t%7s\n", + storeAppendPrintf(sentry, "%7s\t%7s\t%7s\t%11s\t%20s\t%9s\t%s\t%7s\t%7s\t%7s\n", "#", "FD", "PID", "# Requests", "# Deferred Requests", + "# Pending", "Flags", "Time", "Offset", "Request"); for (link = hlp->servers.head; link; link = link->next) { - srv = link->data; - tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time); - storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%20d\t%c%c%c%c%c%c\t%7.3f\t%7d\t%s\n", + helper_stateful_server *srv = link->data; + double tt = srv->requests[0] ? 0.001 * + tvSubMsec(srv->requests[0]->dispatch_time, current_time) : 0.0; + storeAppendPrintf(sentry, "%7d\t%7d\t%7d\t%11d\t%20d\t%9d\t%c%c%c%c%c%c\t%7.3f\t%7d\t%s\n", srv->index + 1, srv->rfd, srv->pid, srv->stats.uses, (int) srv->deferred_requests, + srv->stats.pending, srv->flags.alive ? 'A' : ' ', - srv->flags.busy ? 'B' : ' ', + srv->stats.pending ? 'B' : ' ', srv->flags.closing ? 'C' : ' ', srv->flags.reserved != S_HELPER_FREE ? 'R' : ' ', srv->flags.shutdown ? 'S' : ' ', - srv->request ? (srv->request->placeholder ? 'P' : ' ') : ' ', + srv->requests[0] ? (srv->requests[0]->placeholder ? 'P' : ' ') : ' ', tt < 0.0 ? 0.0 : tt, - (int) srv->offset, - srv->request ? log_quote(srv->request->buf) : "(none)"); + (int) srv->roffset, + srv->requests[0] ? log_quote(srv->requests[0]->buf) : "(none)"); } storeAppendPrintf(sentry, "\nFlags key:\n\n"); storeAppendPrintf(sentry, " A = ALIVE\n"); @@ -528,7 +541,7 @@ continue; } srv->flags.shutdown = 1; /* request it to shut itself down */ - if (srv->flags.busy) { + if (srv->flags.writing) { debug(84, 3) ("helperShutdown: %s #%d is BUSY.\n", hlp->id_name, srv->index + 1); continue; @@ -541,7 +554,10 @@ srv->flags.closing = 1; wfd = srv->wfd; srv->wfd = -1; - comm_close(wfd); + if (wfd == srv->rfd) + shutdown(wfd, 1); + else + comm_close(wfd); } } @@ -560,7 +576,7 @@ continue; } srv->flags.shutdown = 1; /* request it to shut itself down */ - if (srv->flags.busy) { + if (srv->stats.pending) { debug(84, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n", hlp->id_name, srv->index + 1); continue; @@ -642,17 +658,26 @@ helper_server *srv = data; helper *hlp = srv->parent; helper_request *r; + int i, concurrency = hlp->concurrency; + if (!concurrency) + concurrency = 1; assert(srv->rfd == fd); - if (srv->buf) { - memFree(srv->buf, MEM_8K_BUF); - srv->buf = NULL; - } - if ((r = srv->request)) { - if (cbdataValid(r->data)) - r->callback(r->data, srv->buf); - helperRequestFree(r); - srv->request = NULL; + if (srv->rbuf) { + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + memFree(srv->rbuf, MEM_8K_BUF); + srv->rbuf = NULL; + } + if (!memBufIsNull(&srv->wqueue)) + memBufClean(&srv->wqueue); + for (i = 0; i < concurrency; i++) { + if ((r = srv->requests[i])) { + if (cbdataValid(r->data)) + r->callback(r->data, NULL); + helperRequestFree(r); + srv->requests[i] = NULL; + } } + safe_free(srv->requests); if (srv->wfd != srv->rfd && srv->wfd != -1) comm_close(srv->wfd); dlinkDelete(&srv->link, &hlp->servers); @@ -679,17 +704,26 @@ helper_stateful_server *srv = data; statefulhelper *hlp = srv->parent; helper_stateful_request *r; + int i, concurrency = hlp->concurrency; + if (!concurrency) + concurrency = 1; assert(srv->rfd == fd); - if (srv->buf) { - memFree(srv->buf, MEM_8K_BUF); - srv->buf = NULL; - } - if ((r = srv->request)) { - if (cbdataValid(r->data)) - r->callback(r->data, srv, srv->buf); - helperStatefulRequestFree(r); - srv->request = NULL; + if (srv->rbuf) { + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + memFree(srv->rbuf, MEM_8K_BUF); + srv->rbuf = NULL; + } + if (!memBufIsNull(&srv->wqueue)) + memBufClean(&srv->wqueue); + for (i = 0; i < concurrency; i++) { + if ((r = srv->requests[i])) { + if (cbdataValid(r->data)) + r->callback(r->data, srv, NULL); + helperStatefulRequestFree(r); + srv->requests[i] = NULL; + } } + safe_free(srv->requests); /* TODO: walk the local queue of requests and carry them all out */ if (srv->wfd != srv->rfd && srv->wfd != -1) comm_close(srv->wfd); @@ -720,53 +754,73 @@ int len; char *t = NULL; helper_server *srv = data; - helper_request *r; helper *hlp = srv->parent; assert(fd == srv->rfd); assert(cbdataValid(data)); statCounter.syscalls.sock.reads++; - len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset); + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + assert(srv->roffset < srv->rbuf_sz); + len = FD_READ_METHOD(fd, srv->rbuf + srv->roffset, srv->rbuf_sz - srv->roffset - 1); fd_bytes(fd, len, FD_READ); debug(84, 5) ("helperHandleRead: %d bytes from %s #%d.\n", len, hlp->id_name, srv->index + 1); - if (len <= 0) { - if (len < 0) - debug(84, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror()); + if (len == 0) { comm_close(fd); return; } - srv->offset += len; - srv->buf[srv->offset] = '\0'; - r = srv->request; - if (r == NULL) { + commSetSelect(fd, COMM_SELECT_READ, helperHandleRead, srv, 0); + if (len < 0) { + if (!ignoreErrno(errno)) { + debug(84, 1) ("helperHandleRead: FD %d read: %s\n", fd, xstrerror()); + comm_close(fd); + } + return; + } + srv->roffset += len; + srv->rbuf[srv->roffset] = '\0'; + debug(84, 9) ("helperHandleRead: '%s'\n", srv->rbuf); + if (!srv->stats.pending) { /* someone spoke without being spoken to */ - debug(84, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes\n", - hlp->id_name, srv->index + 1, len); - srv->offset = 0; - } else if ((t = strchr(srv->buf, '\n'))) { + debug(84, 1) ("helperHandleRead: unexpected read from %s #%d, %d bytes '%s'\n", + hlp->id_name, srv->index + 1, len, srv->rbuf); + srv->roffset = 0; + srv->rbuf[0] = '\0'; + } + while ((t = strchr(srv->rbuf, '\n'))) { + helper_request *r; + char *msg = srv->rbuf; + int i = 0; /* end of reply found */ - debug(84, 3) ("helperHandleRead: end of reply found\n"); - *t = '\0'; - if (cbdataValid(r->data)) - r->callback(r->data, srv->buf); - srv->flags.busy = 0; - srv->offset = 0; - helperRequestFree(r); - srv->request = NULL; - hlp->stats.replies++; - srv->answer_time = current_time; - hlp->stats.avg_svc_time = - intAverage(hlp->stats.avg_svc_time, - tvSubUsec(srv->dispatch_time, current_time), - hlp->stats.replies, REDIRECT_AV_FACTOR); - if (srv->flags.shutdown) { - int wfd = srv->wfd; - srv->wfd = -1; - comm_close(wfd); - } else - helperKickQueue(hlp); + *t++ = '\0'; + debug(84, 3) ("helperHandleRead: end of reply found: %s\n", srv->rbuf); + if (hlp->concurrency) { + i = strtol(msg, &msg, 10); + while (*msg && isspace(*msg)) + msg++; + } + r = srv->requests[i]; + if (r) { + srv->requests[i] = NULL; + if (cbdataValid(r->data)) + r->callback(r->data, msg); + srv->stats.pending--; + hlp->stats.replies++; + hlp->stats.avg_svc_time = + intAverage(hlp->stats.avg_svc_time, + tvSubUsec(r->dispatch_time, current_time), + hlp->stats.replies, REDIRECT_AV_FACTOR); + helperRequestFree(r); + } else { + debug(84, 1) ("helperHandleRead: unexpected reply on channel %d from %s #%d '%s'\n", + i, hlp->id_name, srv->index + 1, srv->rbuf); + } + srv->roffset -= (t - srv->rbuf); + memmove(srv->rbuf, t, srv->roffset + 1); + } + if (srv->flags.shutdown && !srv->stats.pending) { + comm_close(fd); } else { - commSetSelect(srv->rfd, COMM_SELECT_READ, helperHandleRead, srv, 0); + helperKickQueue(hlp); } } @@ -776,35 +830,52 @@ int len; char *t = NULL; helper_stateful_server *srv = data; - helper_stateful_request *r; statefulhelper *hlp = srv->parent; assert(fd == srv->rfd); assert(cbdataValid(data)); statCounter.syscalls.sock.reads++; - len = FD_READ_METHOD(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset); + /* XXX srv->rbuf should really be a memAllocBuf(), but thisis 2.5.. */ + assert(srv->roffset < srv->rbuf_sz); + len = FD_READ_METHOD(fd, srv->rbuf + srv->roffset, srv->rbuf_sz - srv->roffset); fd_bytes(fd, len, FD_READ); debug(84, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n", len, hlp->id_name, srv->index + 1); - if (len <= 0) { - if (len < 0) - debug(84, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror()); + if (len == 0) { comm_close(fd); return; } - srv->offset += len; - srv->buf[srv->offset] = '\0'; - r = srv->request; - if (r == NULL) { + commSetSelect(fd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0); + if (len < 0) { + if (!ignoreErrno(errno)) { + debug(84, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror()); + comm_close(fd); + } + return; + } + srv->roffset += len; + srv->rbuf[srv->roffset] = '\0'; + if (!srv->stats.pending) { /* someone spoke without being spoken to */ debug(84, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n", hlp->id_name, srv->index + 1, len); - srv->offset = 0; - } else if ((t = strchr(srv->buf, '\n'))) { - /* end of reply found */ + srv->roffset = 0; + srv->rbuf[srv->roffset] = '\0'; + } + while ((t = strchr(srv->rbuf, '\n'))) { + helper_stateful_request *r; + char *msg = srv->rbuf; + int i = 0; debug(84, 3) ("helperStatefulHandleRead: end of reply found\n"); - *t = '\0'; + *t++ = '\0'; + if (hlp->concurrency) { + i = strtol(msg, &msg, 10); + while (*msg && isspace(*msg)) + msg++; + } + r = srv->requests[i]; + srv->requests[i] = NULL; if (cbdataValid(r->data)) { - switch ((r->callback(r->data, srv, srv->buf))) { /*if non-zero reserve helper */ + switch ((r->callback(r->data, srv, msg))) { /*if non-zero reserve helper */ case S_HELPER_UNKNOWN: fatal("helperStatefulHandleRead: either a non-state aware callback was give to the stateful helper routines, or an uninitialised callback response was recieved.\n"); break; @@ -844,29 +915,25 @@ } else { debug(84, 1) ("StatefulHandleRead: no callback data registered\n"); } - srv->flags.busy = 0; - srv->offset = 0; - helperStatefulRequestFree(r); - srv->request = NULL; + srv->roffset -= (t - srv->rbuf); + memmove(srv->rbuf, t, srv->roffset); + srv->stats.pending--; hlp->stats.replies++; hlp->stats.avg_svc_time = intAverage(hlp->stats.avg_svc_time, - tvSubMsec(srv->dispatch_time, current_time), + tvSubMsec(r->dispatch_time, current_time), hlp->stats.replies, REDIRECT_AV_FACTOR); - if (srv->flags.shutdown - && srv->flags.reserved == S_HELPER_FREE - && !srv->deferred_requests) { - int wfd = srv->wfd; - srv->wfd = -1; - comm_close(wfd); - } else { - if (srv->queue.head) - helperStatefulServerKickQueue(srv); - else - helperStatefulKickQueue(hlp); - } + helperStatefulRequestFree(r); + } + if (srv->flags.shutdown + && srv->flags.reserved == S_HELPER_FREE + && !srv->deferred_requests && !srv->stats.pending) { + comm_close(fd); } else { - commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0); + if (srv->queue.head) + helperStatefulServerKickQueue(srv); + else + helperStatefulKickQueue(hlp); } } @@ -980,68 +1047,149 @@ GetFirstAvailable(helper * hlp) { dlink_node *n; - helper_server *srv = NULL; + helper_server *srv; + helper_server *selected = NULL; if (hlp->n_running == 0) return NULL; + /* Find "least" loaded helper (approx) */ for (n = hlp->servers.head; n != NULL; n = n->next) { srv = n->data; - if (srv->flags.busy) + if (selected && selected->stats.pending <= srv->stats.pending) continue; if (!srv->flags.alive) continue; - return srv; + if (srv->flags.shutdown) + continue; + if (selected) { + selected = srv; + break; + } + selected = srv; + if (!selected->stats.pending) + break; } - return NULL; + /* Check for overload */ + if (!selected) + return NULL; + if (selected->stats.pending >= (hlp->concurrency ? hlp->concurrency : 1)) + return NULL; + + return selected; } static helper_stateful_server * StatefulGetFirstAvailable(statefulhelper * hlp) { dlink_node *n; - helper_stateful_server *srv = NULL; + helper_stateful_server *srv; + helper_stateful_server *selected = NULL; debug(84, 5) ("StatefulGetFirstAvailable: Running servers %d.\n", hlp->n_running); if (hlp->n_running == 0) return NULL; + /* Find "least" loaded helper (approx) */ for (n = hlp->servers.head; n != NULL; n = n->next) { srv = n->data; - if (srv->flags.busy) + if (selected && selected->stats.pending <= srv->stats.pending) continue; if (srv->flags.reserved == S_HELPER_RESERVED) continue; if (!srv->flags.alive) continue; + if (srv->flags.shutdown) + continue; if ((hlp->IsAvailable != NULL) && (srv->data != NULL) && !(hlp->IsAvailable(srv->data))) continue; - return srv; + if (selected) { + selected = srv; + break; + } + selected = srv; + if (!selected->stats.pending) + break; } - debug(84, 5) ("StatefulGetFirstAvailable: None available.\n"); - return NULL; + /* Check for overload */ + if (!selected) + return NULL; + if (selected->stats.pending >= (hlp->concurrency ? hlp->concurrency : 1)) + return NULL; + + return selected; } static void +helperDispatch_done(int fd, char *buf, size_t size, int status, void *data) +{ + helper_server *srv = data; + if (status != COMM_OK) { + /* Helper server has crashed.. */ + debug(84, 0)("ERROR: Helper on fd %d has crashed!\n", fd); + } else if (!memBufIsNull(&srv->wqueue)) { + MemBuf mb = srv->wqueue; + srv->wqueue = MemBufNull; + comm_write_mbuf(srv->wfd, + mb, + helperDispatch_done, /* Handler */ + srv); + } else { + helper *hlp = srv->parent; + srv->flags.writing = 0; /* done */ + if (srv->flags.shutdown) { + int wfd; + debug(84, 3) ("helperDispatch: %s #%d is shutting down.\n", + hlp->id_name, srv->index + 1); + if (srv->flags.closing) { + debug(84, 3) ("helperDispatch: %s #%d is CLOSING.\n", + hlp->id_name, srv->index + 1); + return; + } + srv->flags.closing = 1; + wfd = srv->wfd; + srv->wfd = -1; + if (wfd == srv->rfd) + shutdown(wfd, 1); + else + comm_close(wfd); + } + } +} + +static void helperDispatch(helper_server * srv, helper_request * r) { helper *hlp = srv->parent; + helper_request **ptr = NULL; + int slot; if (!cbdataValid(r->data)) { debug(84, 1) ("helperDispatch: invalid callback data\n"); helperRequestFree(r); return; } - assert(!srv->flags.busy); - srv->flags.busy = 1; - srv->request = r; - srv->dispatch_time = current_time; - comm_write(srv->wfd, - r->buf, - strlen(r->buf), - NULL, /* Handler */ - NULL, /* Handler-data */ - NULL); /* free */ - commSetSelect(srv->rfd, - COMM_SELECT_READ, - helperHandleRead, - srv, 0); + for (slot = 0; slot < (hlp->concurrency ? hlp->concurrency : 1); slot++) { + if (!srv->requests[slot]) { + ptr = &srv->requests[slot]; + break; + } + } + assert(ptr); + *ptr = r; + srv->stats.pending += 1; + r->dispatch_time = current_time; + if (memBufIsNull(&srv->wqueue)) + memBufDefInit(&srv->wqueue); + if (hlp->concurrency) + memBufPrintf(&srv->wqueue, "%d %s", slot, r->buf); + else + memBufAppend(&srv->wqueue, r->buf, strlen(r->buf)); + if (!srv->flags.writing) { + MemBuf mb = srv->wqueue; + srv->wqueue = MemBufNull; + srv->flags.writing = 1; + comm_write_mbuf(srv->wfd, + mb, + helperDispatch_done, /* Handler */ + srv); + } debug(84, 5) ("helperDispatch: Request sent to %s #%d, %d bytes\n", hlp->id_name, srv->index + 1, (int) strlen(r->buf)); srv->stats.uses++; @@ -1049,9 +1197,31 @@ } static void +helperStatefulDispatch_done(int fd, char *buf, size_t size, int status, void *data) +{ + helper_stateful_server *srv = data; + if (status != COMM_OK) { + /* Helper server has crashed.. */ + debug(84, 0)("ERROR: Helper on fd %d has crashed!\n", fd); + } else if (!memBufIsNull(&srv->wqueue)) { + MemBuf mb = srv->wqueue; + srv->wqueue = MemBufNull; + srv->flags.writing = 1; + comm_write_mbuf(srv->wfd, + mb, + helperDispatch_done, /* Handler */ + srv); + } else { + srv->flags.writing = 0; /* done */ + } +} + +static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r) { statefulhelper *hlp = srv->parent; + helper_stateful_request **ptr = NULL; + int slot; if (!cbdataValid(r->data)) { debug(84, 1) ("helperStatefulDispatch: invalid callback data\n"); helperStatefulRequestFree(r); @@ -1065,6 +1235,7 @@ r->callback(r->data, srv, NULL); /* throw away the placeholder */ helperStatefulRequestFree(r); +#if 0 /* and push the queue. Note that the callback may have submitted a new * request to the helper which is why we test for the request*/ if (srv->request == NULL) { @@ -1081,21 +1252,34 @@ helperStatefulKickQueue(hlp); } } +#endif return; } - srv->flags.busy = 1; - srv->request = r; - srv->dispatch_time = current_time; - comm_write(srv->wfd, - r->buf, - strlen(r->buf), - NULL, /* Handler */ - NULL, /* Handler-data */ - NULL); /* free */ - commSetSelect(srv->rfd, - COMM_SELECT_READ, - helperStatefulHandleRead, - srv, 0); + for (slot = 0; slot < (hlp->concurrency ? hlp->concurrency : 1); slot++) { + if (!srv->requests[slot]) { + ptr = &srv->requests[slot]; + break; + } + } + assert(ptr); + *ptr = r; + srv->stats.pending++; + r->dispatch_time = current_time; + if (memBufIsNull(&srv->wqueue)) + memBufDefInit(&srv->wqueue); + if (hlp->concurrency) + memBufPrintf(&srv->wqueue, "%d %s", slot, r->buf); + else + memBufAppend(&srv->wqueue, r->buf, strlen(r->buf)); + if (!srv->flags.writing) { + MemBuf mb = srv->wqueue; + srv->wqueue = MemBufNull; + srv->flags.writing = 1; + comm_write_mbuf(srv->wfd, + mb, + helperStatefulDispatch_done, + srv); + } debug(84, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n", hlp->id_name, srv->index + 1, (int) strlen(r->buf)); srv->stats.uses++; Index: squid/src/protos.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/protos.h,v retrieving revision 1.41.6.17 retrieving revision 1.41.6.17.2.1 diff -u -r1.41.6.17 -r1.41.6.17.2.1 --- squid/src/protos.h 15 Dec 2003 03:13:47 -0000 1.41.6.17 +++ squid/src/protos.h 18 Jan 2004 01:09:40 -0000 1.41.6.17.2.1 @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.41.6.17 2003/12/15 03:13:47 squidadm Exp $ + * $Id: protos.h,v 1.41.6.17.2.1 2004/01/18 01:09:40 hno Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -1345,5 +1345,7 @@ extern void externalAclInit(void); extern void externalAclShutdown(void); extern char *strtokFile(void); +const char *externalAclMessage(external_acl_entry * entry); + #endif /* SQUID_PROTOS_H */ Index: squid/src/redirect.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/redirect.c,v retrieving revision 1.7.52.3 retrieving revision 1.7.52.3.2.1 diff -u -r1.7.52.3 -r1.7.52.3.2.1 --- squid/src/redirect.c 15 Dec 2003 03:13:47 -0000 1.7.52.3 +++ squid/src/redirect.c 18 Jan 2004 01:09:40 -0000 1.7.52.3.2.1 @@ -1,6 +1,6 @@ /* - * $Id: redirect.c,v 1.7.52.3 2003/12/15 03:13:47 squidadm Exp $ + * $Id: redirect.c,v 1.7.52.3.2.1 2004/01/18 01:09:40 hno Exp $ * * DEBUG: section 61 Redirector * AUTHOR: Duane Wessels @@ -141,6 +141,7 @@ redirectors = helperCreate("redirector"); redirectors->cmdline = Config.Program.redirect; redirectors->n_to_start = Config.redirectChildren; + redirectors->concurrency = Config.redirectConcurrency; redirectors->ipc_type = IPC_TCP_SOCKET; helperOpenServers(redirectors); if (!init) { Index: squid/src/structs.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/structs.h,v retrieving revision 1.48.2.20 retrieving revision 1.48.2.20.2.1 diff -u -r1.48.2.20 -r1.48.2.20.2.1 --- squid/src/structs.h 15 Jan 2004 03:13:54 -0000 1.48.2.20 +++ squid/src/structs.h 18 Jan 2004 01:09:40 -0000 1.48.2.20.2.1 @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.48.2.20 2004/01/15 03:13:54 squidadm Exp $ + * $Id: structs.h,v 1.48.2.20.2.1 2004/01/18 01:09:40 hno Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -488,6 +488,7 @@ int dnsChildren; #endif int redirectChildren; + int redirectConcurrency; time_t authenticateGCInterval; time_t authenticateTTL; time_t authenticateIpTTL; @@ -1665,6 +1666,8 @@ char *peer_login; /* Configured peer login:password */ time_t lastmod; /* Used on refreshes */ const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ +#define HAVE_EXTACL_LOG 1 + String extacl_log; /* String to be used for access.log purposes */ }; struct _cachemgr_passwd { @@ -1995,6 +1998,7 @@ char *buf; HLPCB *callback; void *data; + struct timeval dispatch_time; }; struct _helper_stateful_request { @@ -2003,6 +2007,7 @@ int placeholder; /* if 1, this is a dummy request waiting for a stateful helper * to become available for deferred requests.*/ void *data; + struct timeval dispatch_time; }; @@ -2014,6 +2019,7 @@ int n_to_start; int n_running; int ipc_type; + int concurrency; time_t last_queue_warn; struct { int requests; @@ -2032,6 +2038,7 @@ int n_to_start; int n_running; int ipc_type; + int concurrency; MemPool *datapool; HLPSAVAIL *IsAvailable; HLPSONEQ *OnEmptyQueue; @@ -2050,22 +2057,22 @@ int pid; int rfd; int wfd; - char *buf; - size_t buf_sz; - off_t offset; - struct timeval dispatch_time; - struct timeval answer_time; + MemBuf wqueue; + char *rbuf; + size_t rbuf_sz; + off_t roffset; dlink_node link; helper *parent; - helper_request *request; + helper_request **requests; struct _helper_flags { + unsigned int writing:1; unsigned int alive:1; - unsigned int busy:1; unsigned int closing:1; unsigned int shutdown:1; } flags; struct { int uses; + unsigned int pending; } stats; }; @@ -2075,18 +2082,17 @@ int pid; int rfd; int wfd; - char *buf; - size_t buf_sz; - off_t offset; - struct timeval dispatch_time; - struct timeval answer_time; + MemBuf wqueue; + char *rbuf; + size_t rbuf_sz; + off_t roffset; dlink_node link; dlink_list queue; statefulhelper *parent; - helper_stateful_request *request; + helper_stateful_request **requests; struct _helper_stateful_flags { + unsigned int writing:1; unsigned int alive:1; - unsigned int busy:1; unsigned int closing:1; unsigned int shutdown:1; stateful_helper_reserve_t reserved; @@ -2097,6 +2103,7 @@ int releases; int deferbyfunc; int deferbycb; + unsigned int pending; } stats; int deferred_requests; /* current number of deferred requests */ void *data; /* State data used by the calling routines */