]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
C++ conversion
[thirdparty/squid.git] / src / store.cc
CommitLineData
164f7660 1
30a4f2a8 2/*
e6ccf245 3 * $Id: store.cc,v 1.548 2002/10/13 20:35:04 robertc Exp $
30a4f2a8 4 *
8638fc66 5 * DEBUG: section 20 Storage Manager
30a4f2a8 6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 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.
30a4f2a8 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.
2b6662ba 24 *
30a4f2a8 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.
2b6662ba 29 *
30a4f2a8 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
c943f331 34 */
090089c4 35
f09f5b26 36#include "squid.h"
e6ccf245 37#include "Store.h"
38#include "StoreClient.h"
090089c4 39
40#define REBUILD_TIMESTAMP_DELTA_MAX 2
227fbb74 41
fcf5283d 42#define STORE_IN_MEM_BUCKETS (229)
090089c4 43
0ee4272b 44const char *memStatusStr[] =
e62d2dea 45{
9dfb6c1c 46 "NOT_IN_MEMORY",
9dfb6c1c 47 "IN_MEMORY"
48};
49
0ee4272b 50const char *pingStatusStr[] =
e62d2dea 51{
f17936ab 52 "PING_NONE",
9dfb6c1c 53 "PING_WAITING",
f17936ab 54 "PING_DONE"
9dfb6c1c 55};
56
0ee4272b 57const char *storeStatusStr[] =
e62d2dea 58{
9dfb6c1c 59 "STORE_OK",
b7fe0ab0 60 "STORE_PENDING"
9dfb6c1c 61};
62
0ee4272b 63const char *swapStatusStr[] =
e62d2dea 64{
8350fe9b 65 "SWAPOUT_NONE",
8350fe9b 66 "SWAPOUT_WRITING",
67 "SWAPOUT_DONE"
9dfb6c1c 68};
69
0a0bf5db 70typedef struct lock_ctrl_t {
582b6456 71 SIH *callback;
0a0bf5db 72 void *callback_data;
73 StoreEntry *e;
74} lock_ctrl_t;
75
65a53c8e 76extern OBJH storeIOStats;
77
e3ef2b09 78/*
79 * local function prototypes
80 */
f5b8bbc4 81static int storeEntryValidLength(const StoreEntry *);
82static void storeGetMemSpace(int);
b93bcace 83static void storeHashDelete(StoreEntry *);
9fb13bb6 84static MemObject *new_MemObject(const char *, const char *);
6cf028ab 85static void destroy_MemObject(StoreEntry *);
ec878047 86static FREE destroy_StoreEntry;
f5b8bbc4 87static void storePurgeMem(StoreEntry *);
6a566b9c 88static void storeEntryReferenced(StoreEntry *);
89static void storeEntryDereferenced(StoreEntry *);
007b8be4 90static int getKeyCounter(void);
8350fe9b 91static int storeKeepInMemory(const StoreEntry *);
8423ff74 92static OBJH storeCheckCachableStats;
e42d5181 93static EVH storeLateRelease;
a21fbb54 94
e3ef2b09 95/*
96 * local variables
97 */
e42d5181 98static Stack LateReleaseStack;
e6ccf245 99MemPool *_StoreEntry::pool = NULL;
100
101void *
102_StoreEntry::operator new (unsigned int bytecount)
103{
104 assert (bytecount == sizeof (_StoreEntry));
105 if (!pool) {
106 pool = memPoolCreate ("StoreEntry", bytecount);
107 memPoolSetChunkSize(pool, 2048 * 1024);
108 }
109 return memPoolAlloc (pool);
110}
111
112void
113_StoreEntry::operator delete (void *address)
114{
115 memPoolFree(pool, address);
116}
117
118size_t
119_StoreEntry::inUseCount()
120{
121 if (!pool)
122 return 0;
123 MemPoolStats stats;
124 memPoolGetStats (&stats, pool);
125 return stats.items_inuse;
126}
127
128size_t
129storeEntryInUse ()
130{
131 return _StoreEntry::inUseCount();
132}
66cedb85 133
bc87dc25 134#if URL_CHECKSUM_DEBUG
135unsigned int
136url_checksum(const char *url)
137{
138 unsigned int ck;
139 MD5_CTX M;
140 static unsigned char digest[16];
141 MD5Init(&M);
142 MD5Update(&M, (unsigned char *) url, strlen(url));
143 MD5Final(digest, &M);
144 xmemcpy(&ck, digest, sizeof(ck));
145 return ck;
146}
147#endif
148
b8d8561b 149static MemObject *
9fb13bb6 150new_MemObject(const char *url, const char *log_url)
227fbb74 151{
e6ccf245 152 MemObject *mem = static_cast<MemObject *>(memAllocate(MEM_MEMOBJECT));
cb69b4c7 153 mem->reply = httpReplyCreate();
9fb13bb6 154 mem->url = xstrdup(url);
bc87dc25 155#if URL_CHECKSUM_DEBUG
156 mem->chksum = url_checksum(mem->url);
157#endif
88738790 158 mem->log_url = xstrdup(log_url);
07304bf9 159 mem->object_sz = -1;
ed03a839 160 mem->fd = -1;
59c4d35b 161 /* XXX account log_url */
a3d5953d 162 debug(20, 3) ("new_MemObject: returning %p\n", mem);
30a4f2a8 163 return mem;
227fbb74 164}
165
f09f5b26 166StoreEntry *
9fb13bb6 167new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
090089c4 168{
169 StoreEntry *e = NULL;
e6ccf245 170 e = new _StoreEntry;
227fbb74 171 if (mem_obj_flag)
9fb13bb6 172 e->mem_obj = new_MemObject(url, log_url);
a3d5953d 173 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
e17dc75c 174 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
cd748f27 175 e->swap_filen = -1;
176 e->swap_dirn = -1;
227fbb74 177 return e;
090089c4 178}
179
b8d8561b 180static void
0e473d70 181destroy_MemObject(StoreEntry * e)
090089c4 182{
6cf028ab 183 MemObject *mem = e->mem_obj;
123abbe1 184 const Ctx ctx = ctx_enter(mem->url);
a3d5953d 185 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
bc87dc25 186#if URL_CHECKSUM_DEBUG
187 assert(mem->chksum == url_checksum(mem->url));
188#endif
41f7e38d 189 e->mem_obj = NULL;
9e665466 190 if (!shutting_down)
2391a162 191 assert(mem->swapout.sio == NULL);
18fe65d0 192 stmemFree(&mem->data_hdr);
193 mem->inmem_hi = 0;
6982a226 194 /*
195 * There is no way to abort FD-less clients, so they might
196 * still have mem->clients set if mem->fd == -1
197 */
06d2839d 198 assert(mem->fd == -1 || mem->clients.head == NULL);
cb69b4c7 199 httpReplyDestroy(mem->reply);
30a4f2a8 200 requestUnlink(mem->request);
201 mem->request = NULL;
2ac76861 202 ctx_exit(ctx); /* must exit before we free mem->url */
48959832 203 safe_free(mem->url);
0e4e0e7d 204 safe_free(mem->log_url); /* XXX account log_url */
1d706003 205 safe_free(mem->vary_headers);
db1cd23c 206 memFree(mem, MEM_MEMOBJECT);
090089c4 207}
208
b8d8561b 209static void
ec878047 210destroy_StoreEntry(void *data)
090089c4 211{
e6ccf245 212 StoreEntry *e = static_cast<StoreEntry *>(data);
a3d5953d 213 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
9e975e4e 214 assert(e != NULL);
e6ccf245 215 if (e == NullStoreEntry::getInstance())
216 return;
227fbb74 217 if (e->mem_obj)
6cf028ab 218 destroy_MemObject(e);
043b055b 219 storeHashDelete(e);
186477c1 220 assert(e->hash.key == NULL);
e6ccf245 221 delete e;
227fbb74 222}
090089c4 223
090089c4 224/* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
225
f09f5b26 226void
9fb13bb6 227storeHashInsert(StoreEntry * e, const cache_key * key)
090089c4 228{
a3d5953d 229 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
9fb13bb6 230 e, storeKeyText(key));
186477c1 231 e->hash.key = storeKeyDup(key);
232 hash_join(store_table, &e->hash);
090089c4 233}
234
b93bcace 235static void
b8d8561b 236storeHashDelete(StoreEntry * e)
090089c4 237{
186477c1 238 hash_remove_link(store_table, &e->hash);
e6ccf245 239 storeKeyFree((unsigned char *)e->hash.key);
186477c1 240 e->hash.key = NULL;
090089c4 241}
242
090089c4 243/* -------------------------------------------------------------------------- */
244
090089c4 245
246/* get rid of memory copy of the object */
620da955 247/* Only call this if storeCheckPurgeMem(e) returns 1 */
24382924 248static void
b8d8561b 249storePurgeMem(StoreEntry * e)
090089c4 250{
2aca8433 251 if (e->mem_obj == NULL)
090089c4 252 return;
9fb13bb6 253 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
e6ccf245 254 storeKeyText((unsigned char *)e->hash.key));
090089c4 255 storeSetMemStatus(e, NOT_IN_MEMORY);
6cf028ab 256 destroy_MemObject(e);
06e8899b 257 if (e->swap_status != SWAPOUT_DONE)
8272aded 258 storeRelease(e);
090089c4 259}
260
6a566b9c 261static void
c1dd71ae 262storeEntryReferenced(StoreEntry * e)
090089c4 263{
cd748f27 264 SwapDir *SD;
265
6a566b9c 266 /* Notify the fs that we're referencing this object again */
267 if (e->swap_dirn > -1) {
a4b8110e 268 SD = INDEXSD(e->swap_dirn);
6a566b9c 269 if (SD->refobj)
270 SD->refobj(SD, e);
271 }
272 /* Notify the memory cache that we're referencing this object again */
c1dd71ae 273 if (e->mem_obj) {
6a566b9c 274 if (mem_policy->Referenced)
275 mem_policy->Referenced(mem_policy, e, &e->mem_obj->repl);
276 }
277}
cd748f27 278
6a566b9c 279static void
c1dd71ae 280storeEntryDereferenced(StoreEntry * e)
6a566b9c 281{
282 SwapDir *SD;
283
284 /* Notify the fs that we're not referencing this object any more */
285 if (e->swap_filen > -1) {
c1dd71ae 286 SD = INDEXSD(e->swap_dirn);
6a566b9c 287 if (SD->unrefobj != NULL)
288 SD->unrefobj(SD, e);
289 }
290 /* Notify the memory cache that we're not referencing this object any more */
c1dd71ae 291 if (e->mem_obj) {
6a566b9c 292 if (mem_policy->Dereferenced)
293 mem_policy->Dereferenced(mem_policy, e, &e->mem_obj->repl);
294 }
295}
296
297void
298storeLockObject(StoreEntry * e)
299{
cd748f27 300 e->lock_count++;
a3d5953d 301 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
e6ccf245 302 storeKeyText((unsigned char *)e->hash.key), (int) e->lock_count);
b8de7ebe 303 e->lastref = squid_curtime;
6a566b9c 304 storeEntryReferenced(e);
090089c4 305}
306
b8d8561b 307void
308storeReleaseRequest(StoreEntry * e)
2285407f 309{
d46a87a8 310 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
58bcd31b 311 return;
e6ccf245 312 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText((unsigned char *)e->hash.key));
d46a87a8 313 EBIT_SET(e->flags, RELEASE_REQUEST);
f3e570e9 314 /*
315 * Clear cachable flag here because we might get called before
316 * anyone else even looks at the cachability flag. Also, this
317 * prevents httpMakePublic from really setting a public key.
318 */
d46a87a8 319 EBIT_CLR(e->flags, ENTRY_CACHABLE);
fe54d06d 320 storeSetPrivateKey(e);
2285407f 321}
322
090089c4 323/* unlock object, return -1 if object get released after unlock
324 * otherwise lock_count */
b8d8561b 325int
326storeUnlockObject(StoreEntry * e)
090089c4 327{
6c895381 328 e->lock_count--;
a3d5953d 329 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
e6ccf245 330 storeKeyText((unsigned char *)e->hash.key), e->lock_count);
30a4f2a8 331 if (e->lock_count)
a1e47288 332 return (int) e->lock_count;
b7fe0ab0 333 if (e->store_status == STORE_PENDING)
d46a87a8 334 EBIT_SET(e->flags, RELEASE_REQUEST);
9e975e4e 335 assert(storePendingNClients(e) == 0);
d46a87a8 336 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
30a4f2a8 337 storeRelease(e);
8350fe9b 338 else if (storeKeepInMemory(e)) {
6a566b9c 339 storeEntryDereferenced(e);
8350fe9b 340 storeSetMemStatus(e, IN_MEMORY);
341 requestUnlink(e->mem_obj->request);
342 e->mem_obj->request = NULL;
66f07209 343 } else {
30a4f2a8 344 storePurgeMem(e);
6a566b9c 345 storeEntryDereferenced(e);
fc8b9fc0 346 if (EBIT_TEST(e->flags, KEY_PRIVATE))
14b9e4c5 347 debug(20, 1) ("WARNING: %s:%d: found KEY_PRIVATE\n", __FILE__, __LINE__);
66f07209 348 }
6c895381 349 return 0;
090089c4 350}
351
2b906e48 352/* Lookup an object in the cache.
090089c4 353 * return just a reference to object, don't start swapping in yet. */
b8d8561b 354StoreEntry *
9fb13bb6 355storeGet(const cache_key * key)
090089c4 356{
88bfe092 357 void *p;
358 PROF_start(storeGet);
9fb13bb6 359 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
88bfe092 360 p = hash_lookup(store_table, key);
361 PROF_stop(storeGet);
362 return (StoreEntry *) p;
090089c4 363}
364
e6ccf245 365void
366_StoreEntry::getPublicByRequestMethod (StoreClient *aClient, request_t * request, const method_t method)
367{
368 assert (aClient);
369 _StoreEntry *result = storeGetPublicByRequestMethod( request, method);
370 if (!result)
371 aClient->created (NullStoreEntry::getInstance());
372 else
373 aClient->created (result);
374}
375
376void
377_StoreEntry::getPublicByRequest (StoreClient *aClient, request_t * request)
378{
379 assert (aClient);
380 _StoreEntry *result = storeGetPublicByRequest (request);
381 if (!result)
382 result = NullStoreEntry::getInstance();
383 aClient->created (result);
384}
385
386void
387_StoreEntry::getPublic (StoreClient *aClient, const char *uri, const method_t method)
388{
389 assert (aClient);
390 _StoreEntry *result = storeGetPublic (uri, method);
391 if (!result)
392 result = NullStoreEntry::getInstance();
393 aClient->created (result);
394}
395
08e5d64f 396StoreEntry *
397storeGetPublic(const char *uri, const method_t method)
398{
6b8e7481 399 return storeGet(storeKeyPublic(uri, method));
08e5d64f 400}
401
f66a9ef4 402StoreEntry *
403storeGetPublicByRequestMethod(request_t * req, const method_t method)
404{
405 return storeGet(storeKeyPublicByRequestMethod(req, method));
406}
407
408StoreEntry *
409storeGetPublicByRequest(request_t * req)
410{
411 StoreEntry *e = storeGetPublicByRequestMethod(req, req->method);
412 if (e == NULL && req->method == METHOD_HEAD)
413 /* We can generate a HEAD reply from a cached GET object */
414 e = storeGetPublicByRequestMethod(req, METHOD_GET);
415 return e;
416}
417
007b8be4 418static int
419getKeyCounter(void)
04e8dbaa 420{
007b8be4 421 static int key_counter = 0;
422 if (++key_counter < 0)
2cc3f720 423 key_counter = 1;
007b8be4 424 return key_counter;
04e8dbaa 425}
426
6c57e268 427void
b8d8561b 428storeSetPrivateKey(StoreEntry * e)
227fbb74 429{
9fb13bb6 430 const cache_key *newkey;
431 MemObject *mem = e->mem_obj;
186477c1 432 if (e->hash.key && EBIT_TEST(e->flags, KEY_PRIVATE))
6eb42cae 433 return; /* is already private */
186477c1 434 if (e->hash.key) {
cd748f27 435 if (e->swap_filen > -1)
b109de6b 436 storeDirSwapLog(e, SWAP_LOG_DEL);
bc0bce21 437 storeHashDelete(e);
b109de6b 438 }
9fb13bb6 439 if (mem != NULL) {
007b8be4 440 mem->id = getKeyCounter();
441 newkey = storeKeyPrivate(mem->url, mem->method, mem->id);
9fb13bb6 442 } else {
007b8be4 443 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter());
9fb13bb6 444 }
445 assert(hash_lookup(store_table, newkey) == NULL);
d46a87a8 446 EBIT_SET(e->flags, KEY_PRIVATE);
8638fc66 447 storeHashInsert(e, newkey);
227fbb74 448}
449
b8d8561b 450void
451storeSetPublicKey(StoreEntry * e)
227fbb74 452{
6eb42cae 453 StoreEntry *e2 = NULL;
9fb13bb6 454 const cache_key *newkey;
455 MemObject *mem = e->mem_obj;
186477c1 456 if (e->hash.key && !EBIT_TEST(e->flags, KEY_PRIVATE))
6eb42cae 457 return; /* is already public */
9fb13bb6 458 assert(mem);
f3e570e9 459 /*
460 * We can't make RELEASE_REQUEST objects public. Depending on
461 * when RELEASE_REQUEST gets set, we might not be swapping out
462 * the object. If we're not swapping out, then subsequent
463 * store clients won't be able to access object data which has
464 * been freed from memory.
d87ebd78 465 *
466 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
467 * be set, and storeSetPublicKey() should not be called.
f3e570e9 468 */
6a566b9c 469#if MORE_DEBUG_OUTPUT
2b906e48 470 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1f38f50a 471 debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n",
186477c1 472 e->hash.key, mem->url);
2b906e48 473#endif
d46a87a8 474 assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
f66a9ef4 475 if (mem->request) {
476 StoreEntry *pe;
477 request_t *request = mem->request;
478 if (!mem->vary_headers) {
479 /* First handle the case where the object no longer varies */
480 safe_free(request->vary_headers);
481 } else {
482 if (request->vary_headers && strcmp(request->vary_headers, mem->vary_headers) != 0) {
483 /* Oops.. the variance has changed. Kill the base object
484 * to record the new variance key
485 */
486 safe_free(request->vary_headers); /* free old "bad" variance key */
487 pe = storeGetPublic(mem->url, mem->method);
488 if (pe)
489 storeRelease(pe);
490 }
491 /* Make sure the request knows the variance status */
a16efaa4 492 if (!request->vary_headers) {
493 const char *vary = httpMakeVaryMark(request, mem->reply);
494 if (vary)
495 request->vary_headers = xstrdup(vary);
496 }
f66a9ef4 497 }
498 if (mem->vary_headers && !storeGetPublic(mem->url, mem->method)) {
499 /* Create "vary" base object */
500 http_version_t version;
501 String vary;
502 pe = storeCreateEntry(mem->url, mem->log_url, request->flags, request->method);
503 httpBuildVersion(&version, 1, 0);
504 httpReplySetHeaders(pe->mem_obj->reply, version, HTTP_OK, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
505 vary = httpHeaderGetList(&mem->reply->header, HDR_VARY);
506 if (strBuf(vary)) {
507 httpHeaderPutStr(&pe->mem_obj->reply->header, HDR_VARY, strBuf(vary));
508 stringClean(&vary);
509 }
510#if X_ACCELERATOR_VARY
511 vary = httpHeaderGetList(&mem->reply->header, HDR_X_ACCELERATOR_VARY);
512 if (strBuf(vary)) {
513 httpHeaderPutStr(&pe->mem_obj->reply->header, HDR_X_ACCELERATOR_VARY, strBuf(vary));
514 stringClean(&vary);
515 }
516#endif
517 storeSetPublicKey(pe);
518 httpReplySwapOut(pe->mem_obj->reply, pe);
519 storeBufferFlush(pe);
520 storeTimestampsSet(pe);
521 storeComplete(pe);
522 storeUnlockObject(pe);
523 }
524 newkey = storeKeyPublicByRequest(mem->request);
525 } else
526 newkey = storeKeyPublic(mem->url, mem->method);
9fb13bb6 527 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
528 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
07622ccd 529 storeSetPrivateKey(e2);
30a4f2a8 530 storeRelease(e2);
f66a9ef4 531 if (mem->request)
532 newkey = storeKeyPublicByRequest(mem->request);
533 else
534 newkey = storeKeyPublic(mem->url, mem->method);
6eb42cae 535 }
186477c1 536 if (e->hash.key)
bc0bce21 537 storeHashDelete(e);
d46a87a8 538 EBIT_CLR(e->flags, KEY_PRIVATE);
8638fc66 539 storeHashInsert(e, newkey);
cd748f27 540 if (e->swap_filen > -1)
b109de6b 541 storeDirSwapLog(e, SWAP_LOG_ADD);
227fbb74 542}
543
b8d8561b 544StoreEntry *
92695e5e 545storeCreateEntry(const char *url, const char *log_url, request_flags flags, method_t method)
090089c4 546{
090089c4 547 StoreEntry *e = NULL;
30a4f2a8 548 MemObject *mem = NULL;
92695e5e 549 debug(20, 3) ("storeCreateEntry: '%s'\n", url);
090089c4 550
f09f5b26 551 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
30a4f2a8 552 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
553 mem = e->mem_obj;
2ac237e2 554 mem->method = method;
92695e5e 555 if (neighbors_do_private_keys || !flags.hierarchical)
86101e40 556 storeSetPrivateKey(e);
557 else
558 storeSetPublicKey(e);
92695e5e 559 if (flags.cachable) {
d46a87a8 560 EBIT_SET(e->flags, ENTRY_CACHABLE);
561 EBIT_CLR(e->flags, RELEASE_REQUEST);
090089c4 562 } else {
d46a87a8 563 EBIT_CLR(e->flags, ENTRY_CACHABLE);
2daae136 564 storeReleaseRequest(e);
090089c4 565 }
234967c9 566 e->store_status = STORE_PENDING;
090089c4 567 storeSetMemStatus(e, NOT_IN_MEMORY);
8350fe9b 568 e->swap_status = SWAPOUT_NONE;
cd748f27 569 e->swap_filen = -1;
570 e->swap_dirn = -1;
090089c4 571 e->refcount = 0;
b8de7ebe 572 e->lastref = squid_curtime;
d20b1cd0 573 e->timestamp = -1; /* set in storeTimestampsSet() */
30a4f2a8 574 e->ping_status = PING_NONE;
d46a87a8 575 EBIT_SET(e->flags, ENTRY_VALIDATED);
090089c4 576 return e;
577}
578
6eb42cae 579/* Mark object as expired */
b8d8561b 580void
581storeExpireNow(StoreEntry * e)
9174e204 582{
e6ccf245 583 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText((unsigned char *)e->hash.key));
b8de7ebe 584 e->expires = squid_curtime;
9174e204 585}
586
090089c4 587/* Append incoming data from a primary server to an entry. */
b8d8561b 588void
b8014333 589storeAppend(StoreEntry * e, const char *buf, int len)
090089c4 590{
3a1c3e2f 591 MemObject *mem = e->mem_obj;
592 assert(mem != NULL);
593 assert(len >= 0);
789f4e81 594 assert(e->store_status == STORE_PENDING);
090089c4 595 if (len) {
d0e2935f 596 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
597 len,
e6ccf245 598 storeKeyText((unsigned char *)e->hash.key));
38792624 599 storeGetMemSpace(len);
18fe65d0 600 stmemAppend(&mem->data_hdr, buf, len);
8350fe9b 601 mem->inmem_hi += len;
090089c4 602 }
d46a87a8 603 if (EBIT_TEST(e->flags, DELAY_SENDING))
d0e2935f 604 return;
605 InvokeHandlers(e);
2391a162 606 storeSwapOut(e);
090089c4 607}
608
b8d8561b 609void
62d32805 610#if STDC_HEADERS
fe4e214f 611storeAppendPrintf(StoreEntry * e, const char *fmt,...)
c30c5a73 612#else
b8d8561b 613storeAppendPrintf(va_alist)
15c05bb0 614 va_dcl
62d32805 615#endif
15c05bb0 616{
62d32805 617#if STDC_HEADERS
618 va_list args;
619 va_start(args, fmt);
620#else
15c05bb0 621 va_list args;
622 StoreEntry *e = NULL;
0ee4272b 623 const char *fmt = NULL;
15c05bb0 624 va_start(args);
625 e = va_arg(args, StoreEntry *);
626 fmt = va_arg(args, char *);
c30c5a73 627#endif
cb69b4c7 628 storeAppendVPrintf(e, fmt, args);
629 va_end(args);
630}
631
632/* used be storeAppendPrintf and Packer */
633void
634storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
635{
636 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 637 buf[0] = '\0';
cb69b4c7 638 vsnprintf(buf, 4096, fmt, vargs);
15c05bb0 639 storeAppend(e, buf, strlen(buf));
c30c5a73 640}
641
8423ff74 642struct _store_check_cachable_hist {
643 struct {
644 int non_get;
645 int not_entry_cachable;
646 int release_request;
647 int wrong_content_length;
648 int negative_cached;
649 int too_big;
d20b1cd0 650 int too_small;
8423ff74 651 int private_key;
c5f627c2 652 int too_many_open_files;
59ffcdf8 653 int too_many_open_fds;
8423ff74 654 } no;
655 struct {
656 int Default;
657 } yes;
658} store_check_cachable_hist;
659
c47511fd 660int
661storeTooManyDiskFilesOpen(void)
662{
663 if (Config.max_open_disk_fds == 0)
664 return 0;
83a29c95 665 if (store_open_disk_fd > Config.max_open_disk_fds)
c47511fd 666 return 1;
667 return 0;
668}
669
d20b1cd0 670static int
671storeCheckTooSmall(StoreEntry * e)
672{
673 MemObject *mem = e->mem_obj;
42b51993 674 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
fbb4a198 675 return 0;
d20b1cd0 676 if (STORE_OK == e->store_status)
e6ccf245 677 if (mem->object_sz < 0 ||
678 static_cast<size_t>(mem->object_sz) < Config.Store.minObjectSize)
d20b1cd0 679 return 1;
680 if (mem->reply->content_length > -1)
681 if (mem->reply->content_length < (int) Config.Store.minObjectSize)
682 return 1;
683 return 0;
684}
685
f09f5b26 686int
8350fe9b 687storeCheckCachable(StoreEntry * e)
6602e70e 688{
2ac237e2 689#if CACHE_ALL_METHODS
690 if (e->mem_obj->method != METHOD_GET) {
8350fe9b 691 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
8423ff74 692 store_check_cachable_hist.no.non_get++;
2ac237e2 693 } else
694#endif
d46a87a8 695 if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
8350fe9b 696 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
8423ff74 697 store_check_cachable_hist.no.not_entry_cachable++;
d46a87a8 698 } else if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
8350fe9b 699 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
8423ff74 700 store_check_cachable_hist.no.release_request++;
d46a87a8 701 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
8350fe9b 702 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
8423ff74 703 store_check_cachable_hist.no.wrong_content_length++;
d46a87a8 704 } else if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
c0dee43f 705 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
8423ff74 706 store_check_cachable_hist.no.negative_cached++;
3e98df20 707 return 0; /* avoid release call below */
c1fc651e 708 } else if ((e->mem_obj->reply->content_length > 0 &&
e6ccf245 709 static_cast<size_t>(e->mem_obj->reply->content_length) > Config.Store.maxObjectSize) ||
710 static_cast<size_t>(e->mem_obj->inmem_hi) > Config.Store.maxObjectSize) {
8350fe9b 711 debug(20, 2) ("storeCheckCachable: NO: too big\n");
8423ff74 712 store_check_cachable_hist.no.too_big++;
7e3ce7b9 713 } else if (e->mem_obj->reply->content_length > (int) Config.Store.maxObjectSize) {
714 debug(20, 2) ("storeCheckCachable: NO: too big\n");
715 store_check_cachable_hist.no.too_big++;
d20b1cd0 716 } else if (storeCheckTooSmall(e)) {
717 debug(20, 2) ("storeCheckCachable: NO: too small\n");
718 store_check_cachable_hist.no.too_small++;
d46a87a8 719 } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
8350fe9b 720 debug(20, 3) ("storeCheckCachable: NO: private key\n");
8423ff74 721 store_check_cachable_hist.no.private_key++;
83a29c95 722 } else if (e->swap_status != SWAPOUT_NONE) {
723 /*
724 * here we checked the swap_status because the remaining
725 * cases are only relevant only if we haven't started swapping
726 * out the object yet.
727 */
728 return 1;
c47511fd 729 } else if (storeTooManyDiskFilesOpen()) {
c5f627c2 730 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
731 store_check_cachable_hist.no.too_many_open_files++;
59ffcdf8 732 } else if (fdNFree() < RESERVED_FD) {
83a29c95 733 debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
59ffcdf8 734 store_check_cachable_hist.no.too_many_open_fds++;
d4432957 735 } else {
8423ff74 736 store_check_cachable_hist.yes.Default++;
6602e70e 737 return 1;
d4432957 738 }
2daae136 739 storeReleaseRequest(e);
d46a87a8 740 EBIT_CLR(e->flags, ENTRY_CACHABLE);
6602e70e 741 return 0;
742}
743
8423ff74 744static void
745storeCheckCachableStats(StoreEntry * sentry)
746{
c40acff3 747 storeAppendPrintf(sentry, "Category\t Count\n");
748
8423ff74 749 storeAppendPrintf(sentry, "no.non_get\t%d\n",
750 store_check_cachable_hist.no.non_get);
751 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
752 store_check_cachable_hist.no.not_entry_cachable);
753 storeAppendPrintf(sentry, "no.release_request\t%d\n",
754 store_check_cachable_hist.no.release_request);
755 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
756 store_check_cachable_hist.no.wrong_content_length);
757 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
758 store_check_cachable_hist.no.negative_cached);
759 storeAppendPrintf(sentry, "no.too_big\t%d\n",
760 store_check_cachable_hist.no.too_big);
d20b1cd0 761 storeAppendPrintf(sentry, "no.too_small\t%d\n",
762 store_check_cachable_hist.no.too_small);
8423ff74 763 storeAppendPrintf(sentry, "no.private_key\t%d\n",
764 store_check_cachable_hist.no.private_key);
c5f627c2 765 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
766 store_check_cachable_hist.no.too_many_open_files);
59ffcdf8 767 storeAppendPrintf(sentry, "no.too_many_open_fds\t%d\n",
768 store_check_cachable_hist.no.too_many_open_fds);
8423ff74 769 storeAppendPrintf(sentry, "yes.default\t%d\n",
770 store_check_cachable_hist.yes.Default);
771}
772
090089c4 773/* Complete transfer into the local cache. */
b8d8561b 774void
775storeComplete(StoreEntry * e)
090089c4 776{
e6ccf245 777 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText((unsigned char *)e->hash.key));
b6403fac 778 if (e->store_status != STORE_PENDING) {
779 /*
780 * if we're not STORE_PENDING, then probably we got aborted
781 * and there should be NO clients on this entry
782 */
7a1024ee 783 assert(EBIT_TEST(e->flags, ENTRY_ABORTED));
784 assert(e->mem_obj->nclients == 0);
b6403fac 785 return;
786 }
07304bf9 787 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
234967c9 788 e->store_status = STORE_OK;
8350fe9b 789 assert(e->mem_status == NOT_IN_MEMORY);
41587298 790 if (!storeEntryValidLength(e)) {
d46a87a8 791 EBIT_SET(e->flags, ENTRY_BAD_LENGTH);
41587298 792 storeReleaseRequest(e);
793 }
6cfa8966 794#if USE_CACHE_DIGESTS
544b1fd4 795 if (e->mem_obj->request)
796 e->mem_obj->request->hier.store_complete_stop = current_time;
39edba21 797#endif
d20b1cd0 798 /*
799 * We used to call InvokeHandlers, then storeSwapOut. However,
800 * Madhukar Reddy <myreddy@persistence.com> reported that
801 * responses without content length would sometimes get released
802 * in client_side, thinking that the response is incomplete.
803 */
2391a162 804 storeSwapOut(e);
d20b1cd0 805 InvokeHandlers(e);
7e3e1d01 806}
807
090089c4 808/*
474cac1b 809 * Someone wants to abort this transfer. Set the reason in the
810 * request structure, call the server-side callback and mark the
2b906e48 811 * entry for releasing
090089c4 812 */
b8d8561b 813void
7197b20d 814storeAbort(StoreEntry * e)
090089c4 815{
3e98df20 816 MemObject *mem = e->mem_obj;
8f39b81d 817 assert(e->store_status == STORE_PENDING);
818 assert(mem != NULL);
e6ccf245 819 debug(20, 6) ("storeAbort: %s\n", storeKeyText((unsigned char *)e->hash.key));
3d02186d 820 storeLockObject(e); /* lock while aborting */
79b5cc5f 821 storeNegativeCache(e);
474cac1b 822 storeReleaseRequest(e);
b7fe0ab0 823 EBIT_SET(e->flags, ENTRY_ABORTED);
8350fe9b 824 storeSetMemStatus(e, NOT_IN_MEMORY);
789f4e81 825 e->store_status = STORE_OK;
496e1d76 826 /*
827 * We assign an object length here. The only other place we assign
828 * the object length is in storeComplete()
829 */
07304bf9 830 mem->object_sz = mem->inmem_hi;
474cac1b 831 /* Notify the server side */
7197b20d 832 if (mem->abort.callback) {
e92e4e44 833 eventAdd("mem->abort.callback",
834 mem->abort.callback,
835 mem->abort.data,
836 0.0,
837 0);
bfcaf585 838 mem->abort.callback = NULL;
6801f8a8 839 mem->abort.data = NULL;
bfcaf585 840 }
474cac1b 841 /* Notify the client side */
090089c4 842 InvokeHandlers(e);
cd748f27 843 /* Close any swapout file */
844 storeSwapOutFileClose(e);
3d02186d 845 storeUnlockObject(e); /* unlock */
090089c4 846}
847
090089c4 848/* Clear Memory storage to accommodate the given object len */
38792624 849static void
d4432957 850storeGetMemSpace(int size)
090089c4 851{
b32508fb 852 StoreEntry *e = NULL;
20cba4b4 853 int released = 0;
b32508fb 854 static time_t last_check = 0;
e954773d 855 int pages_needed;
6a566b9c 856 RemovalPurgeWalker *walker;
b32508fb 857 if (squid_curtime == last_check)
38792624 858 return;
b32508fb 859 last_check = squid_curtime;
e954773d 860 pages_needed = (size / SM_PAGE_SIZE) + 1;
d1a68966 861 if (memInUse(MEM_MEM_NODE) + pages_needed < store_pages_max)
38792624 862 return;
a3d5953d 863 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
6a566b9c 864 /* XXX what to set as max_scan here? */
865 walker = mem_policy->PurgeInit(mem_policy, 100000);
c1dd71ae 866 while ((e = walker->Next(walker))) {
2b906e48 867 storePurgeMem(e);
20cba4b4 868 released++;
d1a68966 869 if (memInUse(MEM_MEM_NODE) + pages_needed < store_pages_max)
8350fe9b 870 break;
871 }
6a566b9c 872 walker->Done(walker);
5e3d4fef 873 debug(20, 3) ("storeGetMemSpace stats:\n");
59c4d35b 874 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
20cba4b4 875 debug(20, 3) (" %6d were released\n", released);
090089c4 876}
877
090089c4 878/* The maximum objects to scan for maintain storage space */
fcefe642 879#define MAINTAIN_MAX_SCAN 1024
880#define MAINTAIN_MAX_REMOVE 64
090089c4 881
2b906e48 882/*
fcefe642 883 * This routine is to be called by main loop in main.c.
884 * It removes expired objects on only one bucket for each time called.
885 * returns the number of objects removed
886 *
887 * This should get called 1/s from main().
888 */
679ac4f0 889void
79d39a72 890storeMaintainSwapSpace(void *datanotused)
090089c4 891{
fc8b9fc0 892 int i;
cd748f27 893 SwapDir *SD;
6a566b9c 894 static time_t last_warn_time = 0;
cd748f27 895
88bfe092 896 PROF_start(storeMaintainSwapSpace);
cd748f27 897 /* walk each fs */
fc8b9fc0 898 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
cd748f27 899 /* call the maintain function .. */
c1dd71ae 900 SD = INDEXSD(i);
6a566b9c 901 /* XXX FixMe: This should be done "in parallell" on the different
902 * cache_dirs, not one at a time.
903 */
c1dd71ae 904 if (SD->maintainfs != NULL)
cd748f27 905 SD->maintainfs(SD);
fc8b9fc0 906 }
6a566b9c 907 if (store_swap_size > Config.Swap.maxSize) {
908 if (squid_curtime - last_warn_time > 10) {
ed19251a 909 debug(20, 0) ("WARNING: Disk space over limit: %ld KB > %ld KB\n",
910 (long int) store_swap_size, (long int) Config.Swap.maxSize);
6a566b9c 911 last_warn_time = squid_curtime;
912 }
913 }
cd748f27 914 /* Reregister a maintain event .. */
915 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
88bfe092 916 PROF_stop(storeMaintainSwapSpace);
090089c4 917}
918
919
920/* release an object from a cache */
6c78a099 921void
b8d8561b 922storeRelease(StoreEntry * e)
090089c4 923{
88bfe092 924 PROF_start(storeRelease);
e6ccf245 925 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText((unsigned char *)e->hash.key));
090089c4 926 /* If, for any reason we can't discard this object because of an
927 * outstanding request, mark it for pending release */
928 if (storeEntryLocked(e)) {
9174e204 929 storeExpireNow(e);
a3d5953d 930 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
2daae136 931 storeReleaseRequest(e);
88bfe092 932 PROF_stop(storeRelease);
6c78a099 933 return;
090089c4 934 }
cd748f27 935 if (store_dirs_rebuilding && e->swap_filen > -1) {
e42d5181 936 storeSetPrivateKey(e);
937 if (e->mem_obj) {
938 storeSetMemStatus(e, NOT_IN_MEMORY);
939 destroy_MemObject(e);
940 }
cd748f27 941 if (e->swap_filen > -1) {
f0b4acef 942 /*
943 * Fake a call to storeLockObject(). When rebuilding is done,
944 * we'll just call storeUnlockObject() on these.
945 */
946 e->lock_count++;
947 EBIT_SET(e->flags, RELEASE_REQUEST);
948 stackPush(&LateReleaseStack, e);
88bfe092 949 PROF_stop(storeRelease);
f0b4acef 950 return;
951 } else {
952 destroy_StoreEntry(e);
953 }
43d9cf56 954 }
a65ff22e 955 storeLog(STORE_LOG_RELEASE, e);
cd748f27 956 if (e->swap_filen > -1) {
957 storeUnlink(e);
a65ff22e 958 if (e->swap_status == SWAPOUT_DONE)
d46a87a8 959 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
cd748f27 960 storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, -1);
d46a87a8 961 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
b109de6b 962 storeDirSwapLog(e, SWAP_LOG_DEL);
cd748f27 963#if 0
964 /* From 2.4. I think we do this in storeUnlink? */
fc8b9fc0 965 storeSwapFileNumberSet(e, -1);
cd748f27 966#endif
090089c4 967 }
8350fe9b 968 storeSetMemStatus(e, NOT_IN_MEMORY);
30a4f2a8 969 destroy_StoreEntry(e);
88bfe092 970 PROF_stop(storeRelease);
090089c4 971}
972
e42d5181 973static void
974storeLateRelease(void *unused)
975{
976 StoreEntry *e;
977 int i;
978 static int n = 0;
b2c141d4 979 if (store_dirs_rebuilding) {
e42d5181 980 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
981 return;
982 }
983 for (i = 0; i < 10; i++) {
e6ccf245 984 e = static_cast<StoreEntry*>(stackPop(&LateReleaseStack));
e42d5181 985 if (e == NULL) {
986 /* done! */
1d8e515f 987 debug(20, 1) ("storeLateRelease: released %d objects\n", n);
e42d5181 988 return;
989 }
990 storeUnlockObject(e);
991 n++;
992 }
993 eventAdd("storeLateRelease", storeLateRelease, NULL, 0.0, 1);
994}
995
090089c4 996/* return 1 if a store entry is locked */
cd748f27 997int
fe4e214f 998storeEntryLocked(const StoreEntry * e)
090089c4 999{
30a4f2a8 1000 if (e->lock_count)
1001 return 1;
8350fe9b 1002 if (e->swap_status == SWAPOUT_WRITING)
30a4f2a8 1003 return 1;
a1e47288 1004 if (e->store_status == STORE_PENDING)
1005 return 1;
b8890359 1006 /*
1007 * SPECIAL, PUBLIC entries should be "locked"
1008 */
d46a87a8 1009 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
b8890359 1010 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
1011 return 1;
30a4f2a8 1012 return 0;
090089c4 1013}
1014
24382924 1015static int
fe4e214f 1016storeEntryValidLength(const StoreEntry * e)
6602e70e 1017{
ffe4a367 1018 int diff;
d8b249ef 1019 const HttpReply *reply;
8a5e92ce 1020 assert(e->mem_obj != NULL);
07304bf9 1021 reply = e->mem_obj->reply;
e6ccf245 1022 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText((unsigned char *)e->hash.key));
07304bf9 1023 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
1024 objectLen(e));
1025 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
1026 reply->hdr_sz);
1027 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
d8b249ef 1028 reply->content_length);
1029 if (reply->content_length < 0) {
1790d392 1030 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
e6ccf245 1031 storeKeyText((unsigned char *)e->hash.key));
ffe4a367 1032 return 1;
1033 }
07304bf9 1034 if (reply->hdr_sz == 0) {
1790d392 1035 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
e6ccf245 1036 storeKeyText((unsigned char *)e->hash.key));
ffe4a367 1037 return 1;
1038 }
ebf4efff 1039 if (e->mem_obj->method == METHOD_HEAD) {
1040 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
e6ccf245 1041 storeKeyText((unsigned char *)e->hash.key));
ebf4efff 1042 return 1;
ffe4a367 1043 }
cb69b4c7 1044 if (reply->sline.status == HTTP_NOT_MODIFIED)
ebf4efff 1045 return 1;
cb69b4c7 1046 if (reply->sline.status == HTTP_NO_CONTENT)
ebf4efff 1047 return 1;
d8b249ef 1048 diff = reply->hdr_sz + reply->content_length - objectLen(e);
ebf4efff 1049 if (diff == 0)
1050 return 1;
1051 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
1052 diff < 0 ? -diff : diff,
e69324cd 1053 diff < 0 ? "big" : "small",
e6ccf245 1054 storeKeyText((unsigned char *)e->hash.key));
ebf4efff 1055 return 0;
ffe4a367 1056}
6602e70e 1057
66cedb85 1058static void
1059storeInitHashValues(void)
1060{
ed19251a 1061 long int i;
a7e59001 1062 /* Calculate size of hash table (maximum currently 64k buckets). */
38792624 1063 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
ed19251a 1064 debug(20, 1) ("Swap maxSize %ld KB, estimated %ld objects\n",
1065 (long int) Config.Swap.maxSize, i);
38792624 1066 i /= Config.Store.objectsPerBucket;
ed19251a 1067 debug(20, 1) ("Target number of buckets: %ld\n", i);
66cedb85 1068 /* ideally the full scan period should be configurable, for the
1069 * moment it remains at approximately 24 hours. */
9fb13bb6 1070 store_hash_buckets = storeKeyHashBuckets(i);
c63ea1e2 1071 debug(20, 1) ("Using %d Store buckets\n", store_hash_buckets);
ed19251a 1072 debug(20, 1) ("Max Mem size: %ld KB\n", (long int) Config.memMaxSize >> 10);
1073 debug(20, 1) ("Max Swap size: %ld KB\n", (long int) Config.Swap.maxSize);
66cedb85 1074}
1075
b8d8561b 1076void
1077storeInit(void)
c943f331 1078{
25535cbe 1079 storeKeyInit();
66cedb85 1080 storeInitHashValues();
9fb13bb6 1081 store_table = hash_create(storeKeyHashCmp,
1082 store_hash_buckets, storeKeyHashHash);
6a566b9c 1083 mem_policy = createRemovalPolicy(Config.memPolicy);
8638fc66 1084 storeDigestInit();
e3ef2b09 1085 storeLogOpen();
e42d5181 1086 stackInit(&LateReleaseStack);
1087 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
9838d6c8 1088 storeDirInit();
b2c141d4 1089 storeRebuildStart();
4cb56589 1090 cachemgrRegister("storedir",
22f3fd98 1091 "Store Directory Stats",
1da3b90b 1092 storeDirStats, 0, 1);
8423ff74 1093 cachemgrRegister("store_check_cachable_stats",
1094 "storeCheckCachable() Stats",
1095 storeCheckCachableStats, 0, 1);
65a53c8e 1096 cachemgrRegister("store_io",
1097 "Store IO Interface Stats",
1098 storeIOStats, 0, 1);
b1c0cc67 1099}
c943f331 1100
b8d8561b 1101void
1102storeConfigure(void)
b1c0cc67 1103{
b1c0cc67 1104 store_swap_high = (long) (((float) Config.Swap.maxSize *
1105 (float) Config.Swap.highWaterMark) / (float) 100);
1106 store_swap_low = (long) (((float) Config.Swap.maxSize *
1107 (float) Config.Swap.lowWaterMark) / (float) 100);
43a70238 1108 store_pages_max = Config.memMaxSize / SM_PAGE_SIZE;
090089c4 1109}
1110
56f29785 1111static int
8350fe9b 1112storeKeepInMemory(const StoreEntry * e)
56f29785 1113{
8350fe9b 1114 MemObject *mem = e->mem_obj;
1115 if (mem == NULL)
56f29785 1116 return 0;
18fe65d0 1117 if (mem->data_hdr.head == NULL)
8350fe9b 1118 return 0;
1119 return mem->inmem_lo == 0;
56f29785 1120}
1121
edce4d98 1122int
1123storeCheckNegativeHit(StoreEntry * e)
1124{
1125 if (!EBIT_TEST(e->flags, ENTRY_NEGCACHED))
1126 return 0;
1127 if (e->expires <= squid_curtime)
1128 return 0;
1129 if (e->store_status != STORE_OK)
1130 return 0;
1131 return 1;
1132}
1133
b8d8561b 1134void
1135storeNegativeCache(StoreEntry * e)
79b5cc5f 1136{
ff5731b1 1137 e->expires = squid_curtime + Config.negativeTtl;
d46a87a8 1138 EBIT_SET(e->flags, ENTRY_NEGCACHED);
79b5cc5f 1139}
0a21bd84 1140
1141void
1142storeFreeMemory(void)
1143{
ec878047 1144 hashFreeItems(store_table, destroy_StoreEntry);
0a21bd84 1145 hashFreeMemory(store_table);
afe95a7e 1146 store_table = NULL;
9bc73deb 1147#if USE_CACHE_DIGESTS
8638fc66 1148 if (store_digest)
1149 cacheDigestDestroy(store_digest);
c68e9c6b 1150#endif
8638fc66 1151 store_digest = NULL;
0a21bd84 1152}
a7e59001 1153
1154int
1155expiresMoreThan(time_t expires, time_t when)
1156{
48f44632 1157 if (expires < 0) /* No Expires given */
1158 return 1;
1159 return (expires > (squid_curtime + when));
a7e59001 1160}
fe54d06d 1161
1162int
1163storeEntryValidToSend(StoreEntry * e)
1164{
d46a87a8 1165 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
fe54d06d 1166 return 0;
d46a87a8 1167 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED))
44f78c24 1168 if (e->expires <= squid_curtime)
fe54d06d 1169 return 0;
b7fe0ab0 1170 if (EBIT_TEST(e->flags, ENTRY_ABORTED))
fe54d06d 1171 return 0;
1172 return 1;
1173}
62663274 1174
ca98227c 1175void
1176storeTimestampsSet(StoreEntry * entry)
1177{
2246b732 1178 const HttpReply *reply = entry->mem_obj->reply;
2f58241d 1179 time_t served_date = reply->date;
efd900cb 1180 int age = httpHeaderGetInt(&reply->header, HDR_AGE);
1181 /*
1182 * The timestamp calculations below tries to mimic the properties
1183 * of the age calculation in RFC2616 section 13.2.3. The implementaion
1184 * isn't complete, and the most notable exception from the RFC is that
1185 * this does not account for response_delay, but it probably does
1186 * not matter much as this is calculated immediately when the headers
1187 * are received, not when the whole response has been received.
1188 */
2f58241d 1189 /* make sure that 0 <= served_date <= squid_curtime */
1190 if (served_date < 0 || served_date > squid_curtime)
cb69b4c7 1191 served_date = squid_curtime;
efd900cb 1192 /*
212cbb48 1193 * Compensate with Age header if origin server clock is ahead
1194 * of us and there is a cache in between us and the origin
1195 * server. But DONT compensate if the age value is larger than
1196 * squid_curtime because it results in a negative served_date.
efd900cb 1197 */
1198 if (age > squid_curtime - served_date)
3c31f135 1199 if (squid_curtime > age)
212cbb48 1200 served_date = squid_curtime - age;
d8b249ef 1201 entry->expires = reply->expires;
1c3e77cd 1202 entry->lastmod = reply->last_modified;
ca98227c 1203 entry->timestamp = served_date;
1204}
429fdbec 1205
bfcaf585 1206void
1207storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
1208{
1209 MemObject *mem = e->mem_obj;
1210 assert(mem);
1211 assert(mem->abort.callback == NULL);
1212 mem->abort.callback = cb;
1213 mem->abort.data = data;
1214}
1215
1216void
1217storeUnregisterAbort(StoreEntry * e)
1218{
1219 MemObject *mem = e->mem_obj;
1220 assert(mem);
1221 mem->abort.callback = NULL;
1222}
88738790 1223
1224void
1225storeMemObjectDump(MemObject * mem)
1226{
18fe65d0 1227 debug(20, 1) ("MemObject->data.head: %p\n",
1228 mem->data_hdr.head);
1229 debug(20, 1) ("MemObject->data.tail: %p\n",
1230 mem->data_hdr.tail);
1231 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1232 mem->data_hdr.origin_offset);
88738790 1233 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
5f6ac48b 1234 (int) mem->start_ping.tv_sec,
1235 (int) mem->start_ping.tv_usec);
8350fe9b 1236 debug(20, 1) ("MemObject->inmem_hi: %d\n",
5f6ac48b 1237 (int) mem->inmem_hi);
8350fe9b 1238 debug(20, 1) ("MemObject->inmem_lo: %d\n",
5f6ac48b 1239 (int) mem->inmem_lo);
88738790 1240 debug(20, 1) ("MemObject->nclients: %d\n",
1241 mem->nclients);
88738790 1242 debug(20, 1) ("MemObject->reply: %p\n",
1243 mem->reply);
1244 debug(20, 1) ("MemObject->request: %p\n",
1245 mem->request);
1246 debug(20, 1) ("MemObject->log_url: %p %s\n",
1247 mem->log_url,
1248 checkNullString(mem->log_url));
1249}
8350fe9b 1250
f09f5b26 1251void
569c4638 1252storeEntryDump(const StoreEntry * e, int l)
d377699f 1253{
e6ccf245 1254 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText((unsigned char *)e->hash.key));
186477c1 1255 debug(20, l) ("StoreEntry->next: %p\n", e->hash.next);
e3ef2b09 1256 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
1257 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
1258 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
1259 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
1260 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
07304bf9 1261 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
e3ef2b09 1262 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
415e0dd2 1263 debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
cd748f27 1264 debug(20, l) ("StoreEntry->swap_dirn: %d\n", (int) e->swap_dirn);
1265 debug(20, l) ("StoreEntry->swap_filen: %d\n", (int) e->swap_filen);
e3ef2b09 1266 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1267 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1268 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1269 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1270 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
d377699f 1271}
1272
1f38f50a 1273/*
1274 * NOTE, this function assumes only two mem states
1275 */
f09f5b26 1276void
e6ccf245 1277storeSetMemStatus(StoreEntry * e, mem_status_t new_status)
8350fe9b 1278{
b93bcace 1279 MemObject *mem = e->mem_obj;
8350fe9b 1280 if (new_status == e->mem_status)
1281 return;
b93bcace 1282 assert(mem != NULL);
1283 if (new_status == IN_MEMORY) {
1284 assert(mem->inmem_lo == 0);
6a566b9c 1285 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1286 debug(20, 4) ("storeSetMemStatus: not inserting special %s into policy\n",
1287 mem->url);
1288 } else {
1289 mem_policy->Add(mem_policy, e, &mem->repl);
1290 debug(20, 4) ("storeSetMemStatus: inserted mem node %s\n",
1291 mem->url);
2b906e48 1292 }
59c4d35b 1293 hot_obj_count++;
b93bcace 1294 } else {
6a566b9c 1295 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
c1dd71ae 1296 debug(20, 4) ("storeSetMemStatus: special entry %s\n",
1297 mem->url);
6a566b9c 1298 } else {
1299 mem_policy->Remove(mem_policy, e, &mem->repl);
1300 debug(20, 4) ("storeSetMemStatus: removed mem node %s\n",
1301 mem->url);
2b906e48 1302 }
59c4d35b 1303 hot_obj_count--;
b93bcace 1304 }
8350fe9b 1305 e->mem_status = new_status;
1306}
6e86c3e8 1307
9fb13bb6 1308const char *
1309storeUrl(const StoreEntry * e)
1310{
1311 if (e == NULL)
24ffafb4 1312 return "[null_entry]";
9fb13bb6 1313 else if (e->mem_obj == NULL)
24ffafb4 1314 return "[null_mem_obj]";
9fb13bb6 1315 else
1316 return e->mem_obj->url;
1317}
24ffafb4 1318
1319void
1320storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1321{
1322 if (e->mem_obj)
1323 return;
1324 e->mem_obj = new_MemObject(url, log_url);
1325}
64763c37 1326
438fc1e3 1327/* this just sets DELAY_SENDING */
1328void
8daca701 1329storeBuffer(StoreEntry * e)
438fc1e3 1330{
d46a87a8 1331 EBIT_SET(e->flags, DELAY_SENDING);
438fc1e3 1332}
1333
1334/* this just clears DELAY_SENDING and Invokes the handlers */
1335void
8daca701 1336storeBufferFlush(StoreEntry * e)
438fc1e3 1337{
d46a87a8 1338 EBIT_CLR(e->flags, DELAY_SENDING);
8daca701 1339 InvokeHandlers(e);
2391a162 1340 storeSwapOut(e);
25535cbe 1341}
07304bf9 1342
e6ccf245 1343ssize_t
07304bf9 1344objectLen(const StoreEntry * e)
1345{
1346 assert(e->mem_obj != NULL);
1347 return e->mem_obj->object_sz;
1348}
1349
1350int
1351contentLen(const StoreEntry * e)
1352{
1353 assert(e->mem_obj != NULL);
1354 assert(e->mem_obj->reply != NULL);
1355 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1356}
f3986a15 1357
1358HttpReply *
1359storeEntryReply(StoreEntry * e)
1360{
1361 if (NULL == e)
3d02186d 1362 return NULL;
f3986a15 1363 if (NULL == e->mem_obj)
3d02186d 1364 return NULL;
f3986a15 1365 return e->mem_obj->reply;
1366}
db1cd23c 1367
1368void
1369storeEntryReset(StoreEntry * e)
1370{
1371 MemObject *mem = e->mem_obj;
1372 debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e));
2391a162 1373 assert(mem->swapout.sio == NULL);
db1cd23c 1374 stmemFree(&mem->data_hdr);
1375 mem->inmem_hi = mem->inmem_lo = 0;
1376 httpReplyDestroy(mem->reply);
1377 mem->reply = httpReplyCreate();
9bc73deb 1378 e->expires = e->lastmod = e->timestamp = -1;
db1cd23c 1379}
2b906e48 1380
cd748f27 1381/*
1382 * storeFsInit
1383 *
1384 * This routine calls the SETUP routine for each fs type.
1385 * I don't know where the best place for this is, and I'm not going to shuffle
1386 * around large chunks of code right now (that can be done once its working.)
1387 */
1388void
1389storeFsInit(void)
1390{
22d38e05 1391 storeReplSetup();
cd748f27 1392 storeFsSetup();
1393}
1394
1395
1396/*
1397 * similar to above, but is called when a graceful shutdown is to occur
1398 * of each fs module.
1399 */
1400void
1401storeFsDone(void)
1402{
1403 int i = 0;
1404
1405 while (storefs_list[i].typestr != NULL) {
a4b8110e 1406 storefs_list[i].donefunc();
cd748f27 1407 i++;
1408 }
1409}
1410
1411/*
1412 * called to add another store fs module
1413 */
a4b8110e 1414void
a2c963ae 1415storeFsAdd(const char *type, STSETUP * setup)
cd748f27 1416{
1417 int i;
1418 /* find the number of currently known storefs types */
1419 for (i = 0; storefs_list && storefs_list[i].typestr; i++) {
a4b8110e 1420 assert(strcmp(storefs_list[i].typestr, type) != 0);
cd748f27 1421 }
1422 /* add the new type */
e6ccf245 1423 storefs_list = static_cast<storefs_entry_t *>(xrealloc(storefs_list, (i + 2) * sizeof(storefs_entry_t)));
a4b8110e 1424 memset(&storefs_list[i + 1], 0, sizeof(storefs_entry_t));
cd748f27 1425 storefs_list[i].typestr = type;
1426 /* Call the FS to set up capabilities and initialize the FS driver */
1427 setup(&storefs_list[i]);
1428}
1429
22d38e05 1430/*
1431 * called to add another store removal policy module
1432 */
1433void
a2c963ae 1434storeReplAdd(const char *type, REMOVALPOLICYCREATE * create)
22d38e05 1435{
1436 int i;
1437 /* find the number of currently known repl types */
1438 for (i = 0; storerepl_list && storerepl_list[i].typestr; i++) {
1439 assert(strcmp(storerepl_list[i].typestr, type) != 0);
1440 }
1441 /* add the new type */
e6ccf245 1442 storerepl_list = static_cast<storerepl_entry_t *>(xrealloc(storerepl_list, (i + 2) * sizeof(storerepl_entry_t)));
22d38e05 1443 memset(&storerepl_list[i + 1], 0, sizeof(storerepl_entry_t));
1444 storerepl_list[i].typestr = type;
1445 storerepl_list[i].create = create;
1446}
1447
1448/*
1449 * Create a removal policy instance
1450 */
1451RemovalPolicy *
1452createRemovalPolicy(RemovalPolicySettings * settings)
1453{
1454 storerepl_entry_t *r;
1455 for (r = storerepl_list; r && r->typestr; r++) {
1456 if (strcmp(r->typestr, settings->type) == 0)
1457 return r->create(settings->args);
1458 }
0c5ccf11 1459 debug(20, 1) ("ERROR: Unknown policy %s\n", settings->type);
1460 debug(20, 1) ("ERROR: Be sure to have set cache_replacement_policy\n");
1461 debug(20, 1) ("ERROR: and memory_replacement_policy in squid.conf!\n");
1462 fatalf("ERROR: Unknown policy %s\n", settings->type);
f0debecb 1463 return NULL; /* NOTREACHED */
22d38e05 1464}
1465
cd748f27 1466#if 0
fc8b9fc0 1467void
1468storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
1469{
1470 if (e->swap_file_number == filn)
1471 return;
1472 if (filn < 0) {
1473 assert(-1 == filn);
1474 storeDirMapBitReset(e->swap_file_number);
1475 storeDirLRUDelete(e);
1476 e->swap_file_number = -1;
1477 } else {
1478 assert(-1 == e->swap_file_number);
1479 storeDirMapBitSet(e->swap_file_number = filn);
1480 storeDirLRUAdd(e);
1481 }
1482}
cd748f27 1483#endif
e6ccf245 1484
1485
1486/* NullStoreEntry */
1487
1488NullStoreEntry NullStoreEntry::_instance;
1489
1490NullStoreEntry *
1491NullStoreEntry::getInstance()
1492{
1493 return &_instance;
1494}