]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
further safegurads in httpMaybeRemovePublic. return if the key
[thirdparty/squid.git] / src / store.cc
CommitLineData
164f7660 1
30a4f2a8 2/*
c5f627c2 3 * $Id: store.cc,v 1.455 1998/09/09 20:05:52 wessels Exp $
30a4f2a8 4 *
8638fc66 5 * DEBUG: section 20 Storage Manager
30a4f2a8 6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
c943f331 34 */
090089c4 35
f09f5b26 36#include "squid.h"
090089c4 37
38#define REBUILD_TIMESTAMP_DELTA_MAX 2
227fbb74 39
fcf5283d 40#define STORE_IN_MEM_BUCKETS (229)
090089c4 41
0ee4272b 42const char *memStatusStr[] =
e62d2dea 43{
9dfb6c1c 44 "NOT_IN_MEMORY",
9dfb6c1c 45 "IN_MEMORY"
46};
47
0ee4272b 48const char *pingStatusStr[] =
e62d2dea 49{
f17936ab 50 "PING_NONE",
9dfb6c1c 51 "PING_WAITING",
52 "PING_TIMEOUT",
f17936ab 53 "PING_DONE"
9dfb6c1c 54};
55
0ee4272b 56const char *storeStatusStr[] =
e62d2dea 57{
9dfb6c1c 58 "STORE_OK",
59 "STORE_PENDING",
60 "STORE_ABORTED"
61};
62
0ee4272b 63const char *swapStatusStr[] =
e62d2dea 64{
8350fe9b 65 "SWAPOUT_NONE",
66 "SWAPOUT_OPENING",
67 "SWAPOUT_WRITING",
68 "SWAPOUT_DONE"
9dfb6c1c 69};
70
0a0bf5db 71typedef struct lock_ctrl_t {
582b6456 72 SIH *callback;
0a0bf5db 73 void *callback_data;
74 StoreEntry *e;
75} lock_ctrl_t;
76
e3ef2b09 77/*
78 * local function prototypes
79 */
01fe9bf4 80static int storeCheckExpired(const StoreEntry *);
f5b8bbc4 81static int storeEntryLocked(const StoreEntry *);
82static int storeEntryValidLength(const StoreEntry *);
83static void storeGetMemSpace(int);
b93bcace 84static void storeHashDelete(StoreEntry *);
9fb13bb6 85static MemObject *new_MemObject(const char *, const char *);
6cf028ab 86static void destroy_MemObject(StoreEntry *);
ec878047 87static FREE destroy_StoreEntry;
f5b8bbc4 88static void storePurgeMem(StoreEntry *);
5ad33356 89static unsigned int getKeyCounter(method_t);
8350fe9b 90static int storeKeepInMemory(const StoreEntry *);
8423ff74 91static OBJH storeCheckCachableStats;
a21fbb54 92
e3ef2b09 93/*
94 * local variables
95 */
b93bcace 96static dlink_list inmem_list;
e954773d 97static int store_pages_high = 0;
98static int store_pages_low = 0;
58104eab 99static int store_swap_high = 0;
100static int store_swap_low = 0;
d1497906 101static int store_swap_mid = 0;
e924600d 102static int store_maintain_rate;
66cedb85 103
b8d8561b 104static MemObject *
9fb13bb6 105new_MemObject(const char *url, const char *log_url)
227fbb74 106{
7021844c 107 MemObject *mem = memAllocate(MEM_MEMOBJECT);
cb69b4c7 108 mem->reply = httpReplyCreate();
9fb13bb6 109 mem->url = xstrdup(url);
88738790 110 mem->log_url = xstrdup(log_url);
8350fe9b 111 mem->swapout.fd = -1;
07304bf9 112 mem->object_sz = -1;
ed03a839 113 mem->fd = -1;
59c4d35b 114 /* XXX account log_url */
a3d5953d 115 debug(20, 3) ("new_MemObject: returning %p\n", mem);
30a4f2a8 116 return mem;
227fbb74 117}
118
f09f5b26 119StoreEntry *
9fb13bb6 120new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
090089c4 121{
122 StoreEntry *e = NULL;
7021844c 123 e = memAllocate(MEM_STOREENTRY);
227fbb74 124 if (mem_obj_flag)
9fb13bb6 125 e->mem_obj = new_MemObject(url, log_url);
a3d5953d 126 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
e17dc75c 127 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
227fbb74 128 return e;
090089c4 129}
130
b8d8561b 131static void
0e473d70 132destroy_MemObject(StoreEntry * e)
090089c4 133{
6cf028ab 134 MemObject *mem = e->mem_obj;
123abbe1 135 const Ctx ctx = ctx_enter(mem->url);
a3d5953d 136 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
41f7e38d 137 e->mem_obj = NULL;
9e665466 138 if (!shutting_down)
e82d6d21 139 assert(mem->swapout.fd == -1);
18fe65d0 140 stmemFree(&mem->data_hdr);
141 mem->inmem_hi = 0;
59c4d35b 142 /* XXX account log_url */
6cf028ab 143#if USE_ASYNC_IO
0e473d70 144 while (mem->clients != NULL)
6cf028ab 145 storeUnregister(e, mem->clients->callback_data);
146#endif
6982a226 147 /*
148 * There is no way to abort FD-less clients, so they might
149 * still have mem->clients set if mem->fd == -1
150 */
151 assert(mem->fd == -1 || mem->clients == NULL);
cb69b4c7 152 httpReplyDestroy(mem->reply);
30a4f2a8 153 requestUnlink(mem->request);
154 mem->request = NULL;
2ac76861 155 ctx_exit(ctx); /* must exit before we free mem->url */
48959832 156 safe_free(mem->url);
157 safe_free(mem->log_url);
3f6c0fb2 158 memFree(MEM_MEMOBJECT, mem);
090089c4 159}
160
b8d8561b 161static void
ec878047 162destroy_StoreEntry(void *data)
090089c4 163{
ec878047 164 StoreEntry *e = data;
a3d5953d 165 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
9e975e4e 166 assert(e != NULL);
227fbb74 167 if (e->mem_obj)
6cf028ab 168 destroy_MemObject(e);
043b055b 169 storeHashDelete(e);
9fb13bb6 170 assert(e->key == NULL);
8c128028 171 memFree(MEM_STOREENTRY, e);
227fbb74 172}
090089c4 173
090089c4 174/* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
175
f09f5b26 176void
9fb13bb6 177storeHashInsert(StoreEntry * e, const cache_key * key)
090089c4 178{
a3d5953d 179 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
9fb13bb6 180 e, storeKeyText(key));
181 e->key = storeKeyDup(key);
b93bcace 182 hash_join(store_table, (hash_link *) e);
66f07209 183 dlinkAdd(e, &e->lru, &store_list);
090089c4 184}
185
b93bcace 186static void
b8d8561b 187storeHashDelete(StoreEntry * e)
090089c4 188{
b93bcace 189 hash_remove_link(store_table, (hash_link *) e);
e3ef2b09 190 dlinkDelete(&e->lru, &store_list);
9fb13bb6 191 storeKeyFree(e->key);
192 e->key = NULL;
090089c4 193}
194
090089c4 195/* -------------------------------------------------------------------------- */
196
090089c4 197
198/* get rid of memory copy of the object */
620da955 199/* Only call this if storeCheckPurgeMem(e) returns 1 */
24382924 200static void
b8d8561b 201storePurgeMem(StoreEntry * e)
090089c4 202{
2aca8433 203 if (e->mem_obj == NULL)
090089c4 204 return;
9fb13bb6 205 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
206 storeKeyText(e->key));
090089c4 207 storeSetMemStatus(e, NOT_IN_MEMORY);
6cf028ab 208 destroy_MemObject(e);
06e8899b 209 if (e->swap_status != SWAPOUT_DONE)
8272aded 210 storeRelease(e);
090089c4 211}
212
0a0bf5db 213void
95c4b18f 214storeLockObject(StoreEntry * e)
090089c4 215{
b93bcace 216 if (e->lock_count++ == 0) {
e3ef2b09 217 dlinkDelete(&e->lru, &store_list);
218 dlinkAdd(e, &e->lru, &store_list);
b93bcace 219 }
a3d5953d 220 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
9fb13bb6 221 storeKeyText(e->key), (int) e->lock_count);
b8de7ebe 222 e->lastref = squid_curtime;
090089c4 223}
224
b8d8561b 225void
226storeReleaseRequest(StoreEntry * e)
2285407f 227{
79a15e0a 228 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
58bcd31b 229 return;
8a5e92ce 230 assert(storeEntryLocked(e));
9fb13bb6 231 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->key));
79a15e0a 232 EBIT_SET(e->flag, RELEASE_REQUEST);
f3e570e9 233 /*
234 * Clear cachable flag here because we might get called before
235 * anyone else even looks at the cachability flag. Also, this
236 * prevents httpMakePublic from really setting a public key.
237 */
6344d3b0 238 EBIT_CLR(e->flag, ENTRY_CACHABLE);
fe54d06d 239 storeSetPrivateKey(e);
2285407f 240}
241
090089c4 242/* unlock object, return -1 if object get released after unlock
243 * otherwise lock_count */
b8d8561b 244int
245storeUnlockObject(StoreEntry * e)
090089c4 246{
6c895381 247 e->lock_count--;
a3d5953d 248 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
9fb13bb6 249 storeKeyText(e->key), e->lock_count);
30a4f2a8 250 if (e->lock_count)
a1e47288 251 return (int) e->lock_count;
5b00be7a 252 if (e->store_status == STORE_PENDING) {
79a15e0a 253 assert(!EBIT_TEST(e->flag, ENTRY_DISPATCHED));
254 EBIT_SET(e->flag, RELEASE_REQUEST);
5b00be7a 255 }
9e975e4e 256 assert(storePendingNClients(e) == 0);
79a15e0a 257 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
30a4f2a8 258 storeRelease(e);
8350fe9b 259 else if (storeKeepInMemory(e)) {
260 storeSetMemStatus(e, IN_MEMORY);
261 requestUnlink(e->mem_obj->request);
262 e->mem_obj->request = NULL;
66f07209 263 } else {
30a4f2a8 264 storePurgeMem(e);
66f07209 265 if (EBIT_TEST(e->flag, KEY_PRIVATE)) {
266 dlinkDelete(&e->lru, &store_list);
267 dlinkAddTail(e, &e->lru, &store_list);
268 }
269 }
6c895381 270 return 0;
090089c4 271}
272
273/* Lookup an object in the cache.
274 * return just a reference to object, don't start swapping in yet. */
b8d8561b 275StoreEntry *
9fb13bb6 276storeGet(const cache_key * key)
090089c4 277{
9fb13bb6 278 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
279 return (StoreEntry *) hash_lookup(store_table, key);
090089c4 280}
281
88738790 282static unsigned int
5ad33356 283getKeyCounter(method_t method)
04e8dbaa 284{
2d25dc1a 285 static unsigned int key_counter = 0;
7cb386d0 286 if (++key_counter == (1 << 24))
2cc3f720 287 key_counter = 1;
5ad33356 288 return (method << 24) | key_counter;
04e8dbaa 289}
290
6c57e268 291void
b8d8561b 292storeSetPrivateKey(StoreEntry * e)
227fbb74 293{
9fb13bb6 294 const cache_key *newkey;
295 MemObject *mem = e->mem_obj;
bc0bce21 296 if (e->key && EBIT_TEST(e->flag, KEY_PRIVATE))
6eb42cae 297 return; /* is already private */
b109de6b 298 if (e->key) {
299 if (e->swap_file_number > -1)
300 storeDirSwapLog(e, SWAP_LOG_DEL);
bc0bce21 301 storeHashDelete(e);
b109de6b 302 }
9fb13bb6 303 if (mem != NULL) {
5ad33356 304 mem->reqnum = getKeyCounter(mem->method);
2ac237e2 305 newkey = storeKeyPrivate(mem->url, mem->method, mem->reqnum);
9fb13bb6 306 } else {
5ad33356 307 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter(METHOD_NONE));
9fb13bb6 308 }
309 assert(hash_lookup(store_table, newkey) == NULL);
79a15e0a 310 EBIT_SET(e->flag, KEY_PRIVATE);
8638fc66 311 storeHashInsert(e, newkey);
227fbb74 312}
313
b8d8561b 314void
315storeSetPublicKey(StoreEntry * e)
227fbb74 316{
6eb42cae 317 StoreEntry *e2 = NULL;
9fb13bb6 318 const cache_key *newkey;
319 MemObject *mem = e->mem_obj;
bc0bce21 320 if (e->key && !EBIT_TEST(e->flag, KEY_PRIVATE))
6eb42cae 321 return; /* is already public */
9fb13bb6 322 assert(mem);
f3e570e9 323 /*
324 * We can't make RELEASE_REQUEST objects public. Depending on
325 * when RELEASE_REQUEST gets set, we might not be swapping out
326 * the object. If we're not swapping out, then subsequent
327 * store clients won't be able to access object data which has
328 * been freed from memory.
329 */
330 assert(!EBIT_TEST(e->flag, RELEASE_REQUEST));
2ac237e2 331 newkey = storeKeyPublic(mem->url, mem->method);
9fb13bb6 332 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
333 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
07622ccd 334 storeSetPrivateKey(e2);
30a4f2a8 335 storeRelease(e2);
2ac237e2 336 newkey = storeKeyPublic(mem->url, mem->method);
6eb42cae 337 }
bc0bce21 338 if (e->key)
339 storeHashDelete(e);
79a15e0a 340 EBIT_CLR(e->flag, KEY_PRIVATE);
8638fc66 341 storeHashInsert(e, newkey);
b109de6b 342 if (e->swap_file_number > -1)
343 storeDirSwapLog(e, SWAP_LOG_ADD);
227fbb74 344}
345
b8d8561b 346StoreEntry *
92695e5e 347storeCreateEntry(const char *url, const char *log_url, request_flags flags, method_t method)
090089c4 348{
090089c4 349 StoreEntry *e = NULL;
30a4f2a8 350 MemObject *mem = NULL;
92695e5e 351 debug(20, 3) ("storeCreateEntry: '%s'\n", url);
090089c4 352
f09f5b26 353 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
30a4f2a8 354 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
355 mem = e->mem_obj;
2ac237e2 356 mem->method = method;
92695e5e 357 if (neighbors_do_private_keys || !flags.hierarchical)
86101e40 358 storeSetPrivateKey(e);
359 else
360 storeSetPublicKey(e);
92695e5e 361 if (flags.cachable) {
79a15e0a 362 EBIT_SET(e->flag, ENTRY_CACHABLE);
363 EBIT_CLR(e->flag, RELEASE_REQUEST);
090089c4 364 } else {
79a15e0a 365 EBIT_CLR(e->flag, ENTRY_CACHABLE);
2daae136 366 storeReleaseRequest(e);
090089c4 367 }
234967c9 368 e->store_status = STORE_PENDING;
090089c4 369 storeSetMemStatus(e, NOT_IN_MEMORY);
8350fe9b 370 e->swap_status = SWAPOUT_NONE;
090089c4 371 e->swap_file_number = -1;
090089c4 372 e->refcount = 0;
b8de7ebe 373 e->lastref = squid_curtime;
ca98227c 374 e->timestamp = 0; /* set in storeTimestampsSet() */
30a4f2a8 375 e->ping_status = PING_NONE;
79a15e0a 376 EBIT_SET(e->flag, ENTRY_VALIDATED);
7e3e1d01 377#ifdef PPNR_WIP
378 EBIT_SET(e->flag, ENTRY_FWD_HDR_WAIT);
379#endif /* PPNR_WIP */
090089c4 380 return e;
381}
382
6eb42cae 383/* Mark object as expired */
b8d8561b 384void
385storeExpireNow(StoreEntry * e)
9174e204 386{
9fb13bb6 387 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->key));
b8de7ebe 388 e->expires = squid_curtime;
9174e204 389}
390
090089c4 391/* Append incoming data from a primary server to an entry. */
b8d8561b 392void
b8014333 393storeAppend(StoreEntry * e, const char *buf, int len)
090089c4 394{
3a1c3e2f 395 MemObject *mem = e->mem_obj;
396 assert(mem != NULL);
397 assert(len >= 0);
090089c4 398 if (len) {
d0e2935f 399 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
400 len,
401 storeKeyText(e->key));
38792624 402 storeGetMemSpace(len);
18fe65d0 403 stmemAppend(&mem->data_hdr, buf, len);
8350fe9b 404 mem->inmem_hi += len;
090089c4 405 }
d0e2935f 406 if (EBIT_TEST(e->flag, DELAY_SENDING))
407 return;
9d66d521 408#ifdef OPTIMISTIC_IO
409 storeLockObject(e);
410#endif
d0e2935f 411 InvokeHandlers(e);
8350fe9b 412 storeCheckSwapOut(e);
9d66d521 413#ifdef OPTIMISTIC_IO
414 storeUnlockObject(e);
415#endif
090089c4 416}
417
24382924 418#ifdef __STDC__
b8d8561b 419void
fe4e214f 420storeAppendPrintf(StoreEntry * e, const char *fmt,...)
c30c5a73 421{
15c05bb0 422 va_list args;
15c05bb0 423 va_start(args, fmt);
c30c5a73 424#else
b8d8561b 425void
426storeAppendPrintf(va_alist)
15c05bb0 427 va_dcl
428{
429 va_list args;
430 StoreEntry *e = NULL;
0ee4272b 431 const char *fmt = NULL;
15c05bb0 432 va_start(args);
433 e = va_arg(args, StoreEntry *);
434 fmt = va_arg(args, char *);
c30c5a73 435#endif
cb69b4c7 436 storeAppendVPrintf(e, fmt, args);
437 va_end(args);
438}
439
440/* used be storeAppendPrintf and Packer */
441void
442storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
443{
444 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 445 buf[0] = '\0';
cb69b4c7 446 vsnprintf(buf, 4096, fmt, vargs);
15c05bb0 447 storeAppend(e, buf, strlen(buf));
c30c5a73 448}
449
8423ff74 450struct _store_check_cachable_hist {
451 struct {
452 int non_get;
453 int not_entry_cachable;
454 int release_request;
455 int wrong_content_length;
456 int negative_cached;
457 int too_big;
458 int private_key;
c5f627c2 459 int too_many_open_files;
8423ff74 460 int lru_age_too_low;
461 } no;
462 struct {
463 int Default;
464 } yes;
465} store_check_cachable_hist;
466
f09f5b26 467int
8350fe9b 468storeCheckCachable(StoreEntry * e)
6602e70e 469{
2ac237e2 470#if CACHE_ALL_METHODS
471 if (e->mem_obj->method != METHOD_GET) {
8350fe9b 472 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
8423ff74 473 store_check_cachable_hist.no.non_get++;
2ac237e2 474 } else
475#endif
476 if (!EBIT_TEST(e->flag, ENTRY_CACHABLE)) {
8350fe9b 477 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
8423ff74 478 store_check_cachable_hist.no.not_entry_cachable++;
79a15e0a 479 } else if (EBIT_TEST(e->flag, RELEASE_REQUEST)) {
8350fe9b 480 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
8423ff74 481 store_check_cachable_hist.no.release_request++;
b34ed725 482 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flag, ENTRY_BAD_LENGTH)) {
8350fe9b 483 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
8423ff74 484 store_check_cachable_hist.no.wrong_content_length++;
79a15e0a 485 } else if (EBIT_TEST(e->flag, ENTRY_NEGCACHED)) {
c0dee43f 486 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
8423ff74 487 store_check_cachable_hist.no.negative_cached++;
3e98df20 488 return 0; /* avoid release call below */
8350fe9b 489 } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
490 debug(20, 2) ("storeCheckCachable: NO: too big\n");
8423ff74 491 store_check_cachable_hist.no.too_big++;
79a15e0a 492 } else if (EBIT_TEST(e->flag, KEY_PRIVATE)) {
8350fe9b 493 debug(20, 3) ("storeCheckCachable: NO: private key\n");
8423ff74 494 store_check_cachable_hist.no.private_key++;
c5f627c2 495 } else if (Config.max_open_disk_fds && open_disk_fd > Config.max_open_disk_fds) {
496 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
497 store_check_cachable_hist.no.too_many_open_files++;
734b57eb 498 } else if (storeExpiredReferenceAge() < 300) {
c8b09b26 499 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
500 storeExpiredReferenceAge());
8423ff74 501 store_check_cachable_hist.no.lru_age_too_low++;
d4432957 502 } else {
8423ff74 503 store_check_cachable_hist.yes.Default++;
6602e70e 504 return 1;
d4432957 505 }
2daae136 506 storeReleaseRequest(e);
79a15e0a 507 EBIT_CLR(e->flag, ENTRY_CACHABLE);
6602e70e 508 return 0;
509}
510
8423ff74 511static void
512storeCheckCachableStats(StoreEntry * sentry)
513{
514 storeAppendPrintf(sentry, "no.non_get\t%d\n",
515 store_check_cachable_hist.no.non_get);
516 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
517 store_check_cachable_hist.no.not_entry_cachable);
518 storeAppendPrintf(sentry, "no.release_request\t%d\n",
519 store_check_cachable_hist.no.release_request);
520 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
521 store_check_cachable_hist.no.wrong_content_length);
522 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
523 store_check_cachable_hist.no.negative_cached);
524 storeAppendPrintf(sentry, "no.too_big\t%d\n",
525 store_check_cachable_hist.no.too_big);
526 storeAppendPrintf(sentry, "no.private_key\t%d\n",
527 store_check_cachable_hist.no.private_key);
c5f627c2 528 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
529 store_check_cachable_hist.no.too_many_open_files);
8423ff74 530 storeAppendPrintf(sentry, "no.lru_age_too_low\t%d\n",
531 store_check_cachable_hist.no.lru_age_too_low);
532 storeAppendPrintf(sentry, "yes.default\t%d\n",
533 store_check_cachable_hist.yes.Default);
534}
535
090089c4 536/* Complete transfer into the local cache. */
b8d8561b 537void
538storeComplete(StoreEntry * e)
090089c4 539{
9fb13bb6 540 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
22955fba 541 assert(e->store_status == STORE_PENDING);
07304bf9 542 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
234967c9 543 e->store_status = STORE_OK;
8350fe9b 544 assert(e->mem_status == NOT_IN_MEMORY);
b34ed725 545 if (!storeEntryValidLength(e))
546 EBIT_SET(e->flag, ENTRY_BAD_LENGTH);
6cfa8966 547#if USE_CACHE_DIGESTS
544b1fd4 548 if (e->mem_obj->request)
549 e->mem_obj->request->hier.store_complete_stop = current_time;
39edba21 550#endif
3a1c3e2f 551 InvokeHandlers(e);
8c123b71 552 storeCheckSwapOut(e);
7e3e1d01 553}
554
1b72bda1 555#ifdef PPNR_WIP
7e3e1d01 556void
e82d6d21 557storePPNR(StoreEntry * e)
7e3e1d01 558{
e82d6d21 559 assert(EBIT_TEST(e->flag, ENTRY_FWD_HDR_WAIT));
560 EBIT_CLR(e->flag, ENTRY_FWD_HDR_WAIT);
090089c4 561}
1b72bda1 562#endif /* PPNR_WIP */
090089c4 563
564/*
474cac1b 565 * Someone wants to abort this transfer. Set the reason in the
566 * request structure, call the server-side callback and mark the
567 * entry for releasing
090089c4 568 */
b8d8561b 569void
9b312a19 570storeAbort(StoreEntry * e, int cbflag)
090089c4 571{
3e98df20 572 MemObject *mem = e->mem_obj;
6801f8a8 573 STABH *callback;
574 void *data;
8f39b81d 575 assert(e->store_status == STORE_PENDING);
576 assert(mem != NULL);
9fb13bb6 577 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
3d02186d 578 storeLockObject(e); /* lock while aborting */
79b5cc5f 579 storeNegativeCache(e);
474cac1b 580 storeReleaseRequest(e);
234967c9 581 e->store_status = STORE_ABORTED;
8350fe9b 582 storeSetMemStatus(e, NOT_IN_MEMORY);
090089c4 583 /* No DISK swap for negative cached object */
8350fe9b 584 e->swap_status = SWAPOUT_NONE;
496e1d76 585 /*
586 * We assign an object length here. The only other place we assign
587 * the object length is in storeComplete()
588 */
07304bf9 589 mem->object_sz = mem->inmem_hi;
474cac1b 590 /* Notify the server side */
591 if (cbflag && mem->abort.callback) {
6801f8a8 592 callback = mem->abort.callback;
593 data = mem->abort.data;
bfcaf585 594 mem->abort.callback = NULL;
6801f8a8 595 mem->abort.data = NULL;
596 callback(data);
bfcaf585 597 }
474cac1b 598 /* Notify the client side */
090089c4 599 InvokeHandlers(e);
6e86c3e8 600 /* Do we need to close the swapout file? */
601 /* Not if we never started swapping out */
6cf028ab 602 /* But we may need to cancel an open/stat in progress if using ASYNC */
603#if USE_ASYNC_IO
604 aioCancel(-1, e);
605#endif
3d02186d 606 if (e->swap_file_number > -1) {
6cf028ab 607#if USE_ASYNC_IO
3d02186d 608 /* Need to cancel any pending ASYNC writes right now */
609 if (mem->swapout.fd >= 0)
610 aioCancel(mem->swapout.fd, NULL);
6cf028ab 611#endif
3d02186d 612 /* we have to close the disk file if there is no write pending */
61038223 613 if (!storeSwapOutWriteQueued(mem))
3d02186d 614 storeSwapOutFileClose(e);
615 }
616 storeUnlockObject(e); /* unlock */
090089c4 617}
618
090089c4 619/* Clear Memory storage to accommodate the given object len */
38792624 620static void
d4432957 621storeGetMemSpace(int size)
090089c4 622{
b32508fb 623 StoreEntry *e = NULL;
20cba4b4 624 int released = 0;
b32508fb 625 static time_t last_check = 0;
e954773d 626 int pages_needed;
b93bcace 627 dlink_node *m;
d6ea85ce 628 dlink_node *head;
79d39a72 629 dlink_node *prev = NULL;
b32508fb 630 if (squid_curtime == last_check)
38792624 631 return;
b32508fb 632 last_check = squid_curtime;
e954773d 633 pages_needed = (size / SM_PAGE_SIZE) + 1;
3f6c0fb2 634 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
38792624 635 return;
311ea387 636 if (store_rebuilding)
38792624 637 return;
a3d5953d 638 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
d6ea85ce 639 head = inmem_list.head;
b93bcace 640 for (m = inmem_list.tail; m; m = prev) {
d6ea85ce 641 if (m == head)
642 break;
8350fe9b 643 prev = m->prev;
b93bcace 644 e = m->data;
d6ea85ce 645 if (storeEntryLocked(e)) {
5999b776 646 dlinkDelete(m, &inmem_list);
647 dlinkAdd(e, m, &inmem_list);
8350fe9b 648 continue;
d6ea85ce 649 }
20cba4b4 650 released++;
2d51a60e 651 storePurgeMem(e);
d6ea85ce 652 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
8350fe9b 653 break;
654 }
5e3d4fef 655 debug(20, 3) ("storeGetMemSpace stats:\n");
59c4d35b 656 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
20cba4b4 657 debug(20, 3) (" %6d were released\n", released);
090089c4 658}
659
090089c4 660/* The maximum objects to scan for maintain storage space */
fcefe642 661#define MAINTAIN_MAX_SCAN 1024
662#define MAINTAIN_MAX_REMOVE 64
090089c4 663
fcefe642 664/*
665 * This routine is to be called by main loop in main.c.
666 * It removes expired objects on only one bucket for each time called.
667 * returns the number of objects removed
668 *
669 * This should get called 1/s from main().
670 */
679ac4f0 671void
79d39a72 672storeMaintainSwapSpace(void *datanotused)
090089c4 673{
b93bcace 674 dlink_node *m;
79d39a72 675 dlink_node *prev = NULL;
a1b0d7cf 676 StoreEntry *e = NULL;
090089c4 677 int scanned = 0;
090089c4 678 int locked = 0;
679ac4f0 679 int expired = 0;
20cba4b4 680 int max_scan;
681 int max_remove;
ec14f197 682 static time_t last_warn_time = 0;
fcefe642 683 /* We can't delete objects while rebuilding swap */
c8b09b26 684 if (store_rebuilding) {
685 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
679ac4f0 686 return;
d1497906 687 } else if (store_swap_size < store_swap_mid) {
688 max_scan = 100;
689 max_remove = 8;
690 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
c8b09b26 691 } else if (store_swap_size < store_swap_high) {
692 max_scan = 200;
693 max_remove = 8;
694 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.1, 1);
20cba4b4 695 } else {
4cb56589 696 max_scan = 500;
c8b09b26 697 max_remove = 32;
698 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.0, 1);
20cba4b4 699 }
fcefe642 700 debug(20, 3) ("storeMaintainSwapSpace\n");
e3ef2b09 701 for (m = store_list.tail; m; m = prev) {
b93bcace 702 prev = m->prev;
703 e = m->data;
d150898a 704 scanned++;
b93bcace 705 if (storeEntryLocked(e)) {
ac2197dc 706 /*
707 * If there is a locked entry at the tail of the LRU list,
708 * move it to the beginning to get it out of the way.
709 * Theoretically, we might have all locked objects at the
710 * tail, and then we'll never remove anything here and the
711 * LRU age will go to zero.
712 */
713 if (memInUse(MEM_STOREENTRY) > max_scan) {
4b4cd312 714 dlinkDelete(&e->lru, &store_list);
715 dlinkAdd(e, &e->lru, &store_list);
ac2197dc 716 }
b93bcace 717 locked++;
01fe9bf4 718 } else if (storeCheckExpired(e)) {
b109de6b 719 expired++;
720 storeRelease(e);
090089c4 721 }
d150898a 722 if (expired >= max_remove)
b93bcace 723 break;
d150898a 724 if (scanned >= max_scan)
b93bcace 725 break;
090089c4 726 }
4cb56589 727 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
728 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
729 debug(20, 3) (" %6d were scanned\n", scanned);
730 debug(20, 3) (" %6d were locked\n", locked);
731 debug(20, 3) (" %6d were expired\n", expired);
fcefe642 732 if (store_swap_size < Config.Swap.maxSize)
b93bcace 733 return;
734 if (squid_curtime - last_warn_time < 10)
735 return;
736 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
737 store_swap_size, Config.Swap.maxSize);
738 last_warn_time = squid_curtime;
090089c4 739}
740
741
742/* release an object from a cache */
cc61958c 743/* return number of objects released. */
6c78a099 744void
b8d8561b 745storeRelease(StoreEntry * e)
090089c4 746{
9fb13bb6 747 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
090089c4 748 /* If, for any reason we can't discard this object because of an
749 * outstanding request, mark it for pending release */
750 if (storeEntryLocked(e)) {
9174e204 751 storeExpireNow(e);
a3d5953d 752 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
2daae136 753 storeReleaseRequest(e);
6c78a099 754 return;
090089c4 755 }
6cf028ab 756#if USE_ASYNC_IO
25535cbe 757 /*
758 * Make sure all forgotten async ops are cancelled
759 */
760 aioCancel(-1, e);
6cf028ab 761#else
311ea387 762 if (store_rebuilding) {
a3d5953d 763 debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n",
9fb13bb6 764 storeUrl(e));
30a4f2a8 765 storeExpireNow(e);
766 storeSetPrivateKey(e);
79a15e0a 767 EBIT_SET(e->flag, RELEASE_REQUEST);
6c78a099 768 return;
30a4f2a8 769 }
6cf028ab 770#endif
a65ff22e 771 storeLog(STORE_LOG_RELEASE, e);
43fee0b8 772 if (e->swap_file_number > -1) {
25535cbe 773 storeUnlinkFileno(e->swap_file_number);
d961be0a 774 storeDirMapBitReset(e->swap_file_number);
a65ff22e 775 if (e->swap_status == SWAPOUT_DONE)
7b3d6868 776 if (EBIT_TEST(e->flag, ENTRY_VALIDATED))
777 storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
b109de6b 778 if (!EBIT_TEST(e->flag, KEY_PRIVATE))
779 storeDirSwapLog(e, SWAP_LOG_DEL);
090089c4 780 }
8350fe9b 781 storeSetMemStatus(e, NOT_IN_MEMORY);
30a4f2a8 782 destroy_StoreEntry(e);
090089c4 783}
784
090089c4 785/* return 1 if a store entry is locked */
24382924 786static int
fe4e214f 787storeEntryLocked(const StoreEntry * e)
090089c4 788{
30a4f2a8 789 if (e->lock_count)
790 return 1;
6cf028ab 791 if (e->swap_status == SWAPOUT_OPENING)
792 return 1;
8350fe9b 793 if (e->swap_status == SWAPOUT_WRITING)
30a4f2a8 794 return 1;
a1e47288 795 if (e->store_status == STORE_PENDING)
796 return 1;
79a15e0a 797 if (EBIT_TEST(e->flag, ENTRY_SPECIAL))
365cb147 798 return 1;
30a4f2a8 799 return 0;
090089c4 800}
801
24382924 802static int
fe4e214f 803storeEntryValidLength(const StoreEntry * e)
6602e70e 804{
ffe4a367 805 int diff;
d8b249ef 806 const HttpReply *reply;
8a5e92ce 807 assert(e->mem_obj != NULL);
07304bf9 808 reply = e->mem_obj->reply;
9fb13bb6 809 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
07304bf9 810 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
811 objectLen(e));
812 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
813 reply->hdr_sz);
814 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
d8b249ef 815 reply->content_length);
816 if (reply->content_length < 0) {
1790d392 817 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
9fb13bb6 818 storeKeyText(e->key));
ffe4a367 819 return 1;
820 }
07304bf9 821 if (reply->hdr_sz == 0) {
1790d392 822 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
9fb13bb6 823 storeKeyText(e->key));
ffe4a367 824 return 1;
825 }
ebf4efff 826 if (e->mem_obj->method == METHOD_HEAD) {
827 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
9fb13bb6 828 storeKeyText(e->key));
ebf4efff 829 return 1;
ffe4a367 830 }
cb69b4c7 831 if (reply->sline.status == HTTP_NOT_MODIFIED)
ebf4efff 832 return 1;
cb69b4c7 833 if (reply->sline.status == HTTP_NO_CONTENT)
ebf4efff 834 return 1;
d8b249ef 835 diff = reply->hdr_sz + reply->content_length - objectLen(e);
ebf4efff 836 if (diff == 0)
837 return 1;
838 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
839 diff < 0 ? -diff : diff,
840 diff < 0 ? "small" : "big",
841 storeKeyText(e->key));
842 return 0;
ffe4a367 843}
6602e70e 844
66cedb85 845static void
846storeInitHashValues(void)
847{
848 int i;
a7e59001 849 /* Calculate size of hash table (maximum currently 64k buckets). */
38792624 850 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
86101e40 851 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
66cedb85 852 Config.Swap.maxSize, i);
38792624 853 i /= Config.Store.objectsPerBucket;
a3d5953d 854 debug(20, 1) ("Target number of buckets: %d\n", i);
66cedb85 855 /* ideally the full scan period should be configurable, for the
856 * moment it remains at approximately 24 hours. */
9fb13bb6 857 store_hash_buckets = storeKeyHashBuckets(i);
24ffafb4 858 store_maintain_rate = 86400 / store_hash_buckets;
9fb13bb6 859 assert(store_maintain_rate > 0);
7454a5c2 860 debug(20, 1) ("Using %d Store buckets, replacement runs every %d second%s\n",
9fb13bb6 861 store_hash_buckets,
66cedb85 862 store_maintain_rate,
863 store_maintain_rate == 1 ? null_string : "s");
a47b9029 864 debug(20, 1) ("Max Mem size: %d KB\n", Config.Mem.maxSize >> 10);
865 debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
66cedb85 866}
867
b8d8561b 868void
869storeInit(void)
c943f331 870{
25535cbe 871 storeKeyInit();
66cedb85 872 storeInitHashValues();
9fb13bb6 873 store_table = hash_create(storeKeyHashCmp,
874 store_hash_buckets, storeKeyHashHash);
8638fc66 875 storeDigestInit();
e3ef2b09 876 storeLogOpen();
85407535 877 if (storeVerifyCacheDirs() < 0) {
d377699f 878 xstrncpy(tmp_error_buf,
879 "\tFailed to verify one of the swap directories, Check cache.log\n"
880 "\tfor details. Run 'squid -z' to create swap directories\n"
881 "\tif needed, or if running Squid for the first time.",
882 ERROR_BUF_SZ);
883 fatal(tmp_error_buf);
884 }
e3ef2b09 885 storeDirOpenSwapLogs();
e3ef2b09 886 store_list.head = store_list.tail = NULL;
b93bcace 887 inmem_list.head = inmem_list.tail = NULL;
5830cdb3 888 storeRebuildStart();
4cb56589 889 cachemgrRegister("storedir",
22f3fd98 890 "Store Directory Stats",
1da3b90b 891 storeDirStats, 0, 1);
8423ff74 892 cachemgrRegister("store_check_cachable_stats",
893 "storeCheckCachable() Stats",
894 storeCheckCachableStats, 0, 1);
b1c0cc67 895}
c943f331 896
b8d8561b 897void
898storeConfigure(void)
b1c0cc67 899{
e954773d 900 int store_mem_high = 0;
901 int store_mem_low = 0;
b6f794d6 902 store_mem_high = (long) (Config.Mem.maxSize / 100) *
903 Config.Mem.highWaterMark;
904 store_mem_low = (long) (Config.Mem.maxSize / 100) *
905 Config.Mem.lowWaterMark;
090089c4 906
b1c0cc67 907 store_swap_high = (long) (((float) Config.Swap.maxSize *
908 (float) Config.Swap.highWaterMark) / (float) 100);
909 store_swap_low = (long) (((float) Config.Swap.maxSize *
910 (float) Config.Swap.lowWaterMark) / (float) 100);
d1497906 911 store_swap_mid = (store_swap_high >> 1) + (store_swap_low >> 1);
e954773d 912
913 store_pages_high = store_mem_high / SM_PAGE_SIZE;
914 store_pages_low = store_mem_low / SM_PAGE_SIZE;
090089c4 915}
916
56f29785 917static int
8350fe9b 918storeKeepInMemory(const StoreEntry * e)
56f29785 919{
8350fe9b 920 MemObject *mem = e->mem_obj;
921 if (mem == NULL)
56f29785 922 return 0;
18fe65d0 923 if (mem->data_hdr.head == NULL)
8350fe9b 924 return 0;
925 return mem->inmem_lo == 0;
56f29785 926}
927
b8d8561b 928static int
01fe9bf4 929storeCheckExpired(const StoreEntry * e)
620da955 930{
931 if (storeEntryLocked(e))
932 return 0;
1148b77c 933 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
934 return 1;
79a15e0a 935 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
7cc1830f 936 return 1;
fcefe642 937 if (squid_curtime - e->lastref > storeExpiredReferenceAge())
66cedb85 938 return 1;
66cedb85 939 return 0;
620da955 940}
941
db9ccd5a 942/*
943 * storeExpiredReferenceAge
944 *
429fdbec 945 * The LRU age is scaled exponentially between 1 minute and
946 * Config.referenceAge , when store_swap_low < store_swap_size <
947 * store_swap_high. This keeps store_swap_size within the low and high
948 * water marks. If the cache is very busy then store_swap_size stays
949 * closer to the low water mark, if it is not busy, then it will stay
950 * near the high water mark. The LRU age value can be examined on the
951 * cachemgr 'info' page.
fbdfb3cf 952 */
35feb4aa 953time_t
58104eab 954storeExpiredReferenceAge(void)
955{
58104eab 956 double x;
957 double z;
958 time_t age;
429fdbec 959 x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
960 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
8ac49cdc 961 z = pow((double) (Config.referenceAge / 60), x);
58104eab 962 age = (time_t) (z * 60.0);
963 if (age < 60)
964 age = 60;
965 else if (age > 31536000)
966 age = 31536000;
967 return age;
0a362c34 968}
2edc2504 969
b8d8561b 970void
971storeNegativeCache(StoreEntry * e)
79b5cc5f 972{
ff5731b1 973 e->expires = squid_curtime + Config.negativeTtl;
79a15e0a 974 EBIT_SET(e->flag, ENTRY_NEGCACHED);
79b5cc5f 975}
0a21bd84 976
977void
978storeFreeMemory(void)
979{
ec878047 980 hashFreeItems(store_table, destroy_StoreEntry);
0a21bd84 981 hashFreeMemory(store_table);
afe95a7e 982 store_table = NULL;
8638fc66 983 if (store_digest)
984 cacheDigestDestroy(store_digest);
985 store_digest = NULL;
0a21bd84 986}
a7e59001 987
988int
989expiresMoreThan(time_t expires, time_t when)
990{
48f44632 991 if (expires < 0) /* No Expires given */
992 return 1;
993 return (expires > (squid_curtime + when));
a7e59001 994}
fe54d06d 995
996int
997storeEntryValidToSend(StoreEntry * e)
998{
79a15e0a 999 if (EBIT_TEST(e->flag, RELEASE_REQUEST))
fe54d06d 1000 return 0;
79a15e0a 1001 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED))
44f78c24 1002 if (e->expires <= squid_curtime)
fe54d06d 1003 return 0;
1004 if (e->store_status == STORE_ABORTED)
1005 return 0;
1006 return 1;
1007}
62663274 1008
ca98227c 1009void
1010storeTimestampsSet(StoreEntry * entry)
1011{
1012 time_t served_date = -1;
2246b732 1013 const HttpReply *reply = entry->mem_obj->reply;
d8b249ef 1014 served_date = reply->date;
cb69b4c7 1015 if (served_date < 0)
1016 served_date = squid_curtime;
d8b249ef 1017 entry->expires = reply->expires;
1c3e77cd 1018 entry->lastmod = reply->last_modified;
ca98227c 1019 entry->timestamp = served_date;
1020}
429fdbec 1021
bfcaf585 1022void
1023storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
1024{
1025 MemObject *mem = e->mem_obj;
1026 assert(mem);
1027 assert(mem->abort.callback == NULL);
1028 mem->abort.callback = cb;
1029 mem->abort.data = data;
1030}
1031
1032void
1033storeUnregisterAbort(StoreEntry * e)
1034{
1035 MemObject *mem = e->mem_obj;
1036 assert(mem);
1037 mem->abort.callback = NULL;
1038}
88738790 1039
1040void
1041storeMemObjectDump(MemObject * mem)
1042{
18fe65d0 1043 debug(20, 1) ("MemObject->data.head: %p\n",
1044 mem->data_hdr.head);
1045 debug(20, 1) ("MemObject->data.tail: %p\n",
1046 mem->data_hdr.tail);
1047 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1048 mem->data_hdr.origin_offset);
88738790 1049 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
5f6ac48b 1050 (int) mem->start_ping.tv_sec,
1051 (int) mem->start_ping.tv_usec);
8350fe9b 1052 debug(20, 1) ("MemObject->inmem_hi: %d\n",
5f6ac48b 1053 (int) mem->inmem_hi);
8350fe9b 1054 debug(20, 1) ("MemObject->inmem_lo: %d\n",
5f6ac48b 1055 (int) mem->inmem_lo);
88738790 1056 debug(20, 1) ("MemObject->clients: %p\n",
1057 mem->clients);
1058 debug(20, 1) ("MemObject->nclients: %d\n",
1059 mem->nclients);
8350fe9b 1060 debug(20, 1) ("MemObject->swapout.fd: %d\n",
1061 mem->swapout.fd);
88738790 1062 debug(20, 1) ("MemObject->reply: %p\n",
1063 mem->reply);
1064 debug(20, 1) ("MemObject->request: %p\n",
1065 mem->request);
1066 debug(20, 1) ("MemObject->log_url: %p %s\n",
1067 mem->log_url,
1068 checkNullString(mem->log_url));
1069}
8350fe9b 1070
f09f5b26 1071void
e3ef2b09 1072storeEntryDump(StoreEntry * e, int l)
d377699f 1073{
e3ef2b09 1074 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->key));
1075 debug(20, l) ("StoreEntry->next: %p\n", e->next);
1076 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
1077 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
1078 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
1079 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
1080 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
07304bf9 1081 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
e3ef2b09 1082 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
1083 debug(20, l) ("StoreEntry->flag: %X\n", e->flag);
1084 debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
1085 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1086 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1087 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1088 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1089 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
d377699f 1090}
1091
8350fe9b 1092/* NOTE, this function assumes only two mem states */
f09f5b26 1093void
8350fe9b 1094storeSetMemStatus(StoreEntry * e, int new_status)
1095{
b93bcace 1096 MemObject *mem = e->mem_obj;
8350fe9b 1097 if (new_status == e->mem_status)
1098 return;
b93bcace 1099 assert(mem != NULL);
1100 if (new_status == IN_MEMORY) {
1101 assert(mem->inmem_lo == 0);
2ac237e2 1102 dlinkAdd(e, &mem->lru, &inmem_list);
59c4d35b 1103 hot_obj_count++;
b93bcace 1104 } else {
2ac237e2 1105 dlinkDelete(&mem->lru, &inmem_list);
59c4d35b 1106 hot_obj_count--;
b93bcace 1107 }
8350fe9b 1108 e->mem_status = new_status;
1109}
6e86c3e8 1110
9fb13bb6 1111const char *
1112storeUrl(const StoreEntry * e)
1113{
1114 if (e == NULL)
24ffafb4 1115 return "[null_entry]";
9fb13bb6 1116 else if (e->mem_obj == NULL)
24ffafb4 1117 return "[null_mem_obj]";
9fb13bb6 1118 else
1119 return e->mem_obj->url;
1120}
24ffafb4 1121
1122void
1123storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1124{
1125 if (e->mem_obj)
1126 return;
1127 e->mem_obj = new_MemObject(url, log_url);
1128}
64763c37 1129
438fc1e3 1130/* this just sets DELAY_SENDING */
1131void
8daca701 1132storeBuffer(StoreEntry * e)
438fc1e3 1133{
8daca701 1134 EBIT_SET(e->flag, DELAY_SENDING);
438fc1e3 1135}
1136
1137/* this just clears DELAY_SENDING and Invokes the handlers */
1138void
8daca701 1139storeBufferFlush(StoreEntry * e)
438fc1e3 1140{
8daca701 1141 EBIT_CLR(e->flag, DELAY_SENDING);
1142 InvokeHandlers(e);
d5bd7c41 1143 storeCheckSwapOut(e);
438fc1e3 1144}
25535cbe 1145
1146void
1147storeUnlinkFileno(int fileno)
1148{
07304bf9 1149 debug(20, 5) ("storeUnlinkFileno: %08X\n", fileno);
25535cbe 1150#if USE_ASYNC_IO
1151 safeunlink(storeSwapFullPath(fileno, NULL), 1);
1152#else
1153 unlinkdUnlink(storeSwapFullPath(fileno, NULL));
1154#endif
1155}
07304bf9 1156
1157int
1158objectLen(const StoreEntry * e)
1159{
1160 assert(e->mem_obj != NULL);
1161 return e->mem_obj->object_sz;
1162}
1163
1164int
1165contentLen(const StoreEntry * e)
1166{
1167 assert(e->mem_obj != NULL);
1168 assert(e->mem_obj->reply != NULL);
1169 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1170}
f3986a15 1171
1172HttpReply *
1173storeEntryReply(StoreEntry * e)
1174{
1175 if (NULL == e)
3d02186d 1176 return NULL;
f3986a15 1177 if (NULL == e->mem_obj)
3d02186d 1178 return NULL;
f3986a15 1179 return e->mem_obj->reply;
1180}