]>
Commit | Line | Data |
---|---|---|
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 | 44 | const char *memStatusStr[] = |
e62d2dea | 45 | { |
9dfb6c1c | 46 | "NOT_IN_MEMORY", |
9dfb6c1c | 47 | "IN_MEMORY" |
48 | }; | |
49 | ||
0ee4272b | 50 | const char *pingStatusStr[] = |
e62d2dea | 51 | { |
f17936ab | 52 | "PING_NONE", |
9dfb6c1c | 53 | "PING_WAITING", |
f17936ab | 54 | "PING_DONE" |
9dfb6c1c | 55 | }; |
56 | ||
0ee4272b | 57 | const char *storeStatusStr[] = |
e62d2dea | 58 | { |
9dfb6c1c | 59 | "STORE_OK", |
b7fe0ab0 | 60 | "STORE_PENDING" |
9dfb6c1c | 61 | }; |
62 | ||
0ee4272b | 63 | const char *swapStatusStr[] = |
e62d2dea | 64 | { |
8350fe9b | 65 | "SWAPOUT_NONE", |
8350fe9b | 66 | "SWAPOUT_WRITING", |
67 | "SWAPOUT_DONE" | |
9dfb6c1c | 68 | }; |
69 | ||
0a0bf5db | 70 | typedef struct lock_ctrl_t { |
582b6456 | 71 | SIH *callback; |
0a0bf5db | 72 | void *callback_data; |
73 | StoreEntry *e; | |
74 | } lock_ctrl_t; | |
75 | ||
65a53c8e | 76 | extern OBJH storeIOStats; |
77 | ||
e3ef2b09 | 78 | /* |
79 | * local function prototypes | |
80 | */ | |
f5b8bbc4 | 81 | static int storeEntryValidLength(const StoreEntry *); |
82 | static void storeGetMemSpace(int); | |
b93bcace | 83 | static void storeHashDelete(StoreEntry *); |
9fb13bb6 | 84 | static MemObject *new_MemObject(const char *, const char *); |
6cf028ab | 85 | static void destroy_MemObject(StoreEntry *); |
ec878047 | 86 | static FREE destroy_StoreEntry; |
f5b8bbc4 | 87 | static void storePurgeMem(StoreEntry *); |
6a566b9c | 88 | static void storeEntryReferenced(StoreEntry *); |
89 | static void storeEntryDereferenced(StoreEntry *); | |
007b8be4 | 90 | static int getKeyCounter(void); |
8350fe9b | 91 | static int storeKeepInMemory(const StoreEntry *); |
8423ff74 | 92 | static OBJH storeCheckCachableStats; |
e42d5181 | 93 | static EVH storeLateRelease; |
a21fbb54 | 94 | |
e3ef2b09 | 95 | /* |
96 | * local variables | |
97 | */ | |
e42d5181 | 98 | static Stack LateReleaseStack; |
e6ccf245 | 99 | MemPool *_StoreEntry::pool = NULL; |
100 | ||
101 | void * | |
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 | ||
112 | void | |
113 | _StoreEntry::operator delete (void *address) | |
114 | { | |
115 | memPoolFree(pool, address); | |
116 | } | |
117 | ||
118 | size_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 | ||
128 | size_t | |
129 | storeEntryInUse () | |
130 | { | |
131 | return _StoreEntry::inUseCount(); | |
132 | } | |
66cedb85 | 133 | |
bc87dc25 | 134 | #if URL_CHECKSUM_DEBUG |
135 | unsigned int | |
136 | url_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 | 149 | static MemObject * |
9fb13bb6 | 150 | new_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 | 166 | StoreEntry * |
9fb13bb6 | 167 | new_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 | 180 | static void |
0e473d70 | 181 | destroy_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 | 209 | static void |
ec878047 | 210 | destroy_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 | 226 | void |
9fb13bb6 | 227 | storeHashInsert(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 | 235 | static void |
b8d8561b | 236 | storeHashDelete(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 | 248 | static void |
b8d8561b | 249 | storePurgeMem(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 | 261 | static void |
c1dd71ae | 262 | storeEntryReferenced(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 | 279 | static void |
c1dd71ae | 280 | storeEntryDereferenced(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 | ||
297 | void | |
298 | storeLockObject(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 | 307 | void |
308 | storeReleaseRequest(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 | 325 | int |
326 | storeUnlockObject(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 | 354 | StoreEntry * |
9fb13bb6 | 355 | storeGet(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 | 365 | void |
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 | ||
376 | void | |
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 | ||
386 | void | |
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 | 396 | StoreEntry * |
397 | storeGetPublic(const char *uri, const method_t method) | |
398 | { | |
6b8e7481 | 399 | return storeGet(storeKeyPublic(uri, method)); |
08e5d64f | 400 | } |
401 | ||
f66a9ef4 | 402 | StoreEntry * |
403 | storeGetPublicByRequestMethod(request_t * req, const method_t method) | |
404 | { | |
405 | return storeGet(storeKeyPublicByRequestMethod(req, method)); | |
406 | } | |
407 | ||
408 | StoreEntry * | |
409 | storeGetPublicByRequest(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 | 418 | static int |
419 | getKeyCounter(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 | 427 | void |
b8d8561b | 428 | storeSetPrivateKey(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 | 450 | void |
451 | storeSetPublicKey(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 | 544 | StoreEntry * |
92695e5e | 545 | storeCreateEntry(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 | 580 | void |
581 | storeExpireNow(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 | 588 | void |
b8014333 | 589 | storeAppend(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 | 609 | void |
62d32805 | 610 | #if STDC_HEADERS |
fe4e214f | 611 | storeAppendPrintf(StoreEntry * e, const char *fmt,...) |
c30c5a73 | 612 | #else |
b8d8561b | 613 | storeAppendPrintf(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 */ | |
633 | void | |
634 | storeAppendVPrintf(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 | 642 | struct _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 | 660 | int |
661 | storeTooManyDiskFilesOpen(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 | 670 | static int |
671 | storeCheckTooSmall(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 | 686 | int |
8350fe9b | 687 | storeCheckCachable(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 | 744 | static void |
745 | storeCheckCachableStats(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 | 774 | void |
775 | storeComplete(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 | 813 | void |
7197b20d | 814 | storeAbort(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 | 849 | static void |
d4432957 | 850 | storeGetMemSpace(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 | 889 | void |
79d39a72 | 890 | storeMaintainSwapSpace(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 | 921 | void |
b8d8561b | 922 | storeRelease(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 | 973 | static void |
974 | storeLateRelease(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 | 997 | int |
fe4e214f | 998 | storeEntryLocked(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 | 1015 | static int |
fe4e214f | 1016 | storeEntryValidLength(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 | 1058 | static void |
1059 | storeInitHashValues(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 | 1076 | void |
1077 | storeInit(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 | 1101 | void |
1102 | storeConfigure(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 | 1111 | static int |
8350fe9b | 1112 | storeKeepInMemory(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 | 1122 | int |
1123 | storeCheckNegativeHit(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 | 1134 | void |
1135 | storeNegativeCache(StoreEntry * e) | |
79b5cc5f | 1136 | { |
ff5731b1 | 1137 | e->expires = squid_curtime + Config.negativeTtl; |
d46a87a8 | 1138 | EBIT_SET(e->flags, ENTRY_NEGCACHED); |
79b5cc5f | 1139 | } |
0a21bd84 | 1140 | |
1141 | void | |
1142 | storeFreeMemory(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 | |
1154 | int | |
1155 | expiresMoreThan(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 | |
1162 | int | |
1163 | storeEntryValidToSend(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 | 1175 | void |
1176 | storeTimestampsSet(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 | 1206 | void |
1207 | storeRegisterAbort(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 | ||
1216 | void | |
1217 | storeUnregisterAbort(StoreEntry * e) | |
1218 | { | |
1219 | MemObject *mem = e->mem_obj; | |
1220 | assert(mem); | |
1221 | mem->abort.callback = NULL; | |
1222 | } | |
88738790 | 1223 | |
1224 | void | |
1225 | storeMemObjectDump(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 | 1251 | void |
569c4638 | 1252 | storeEntryDump(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 | 1276 | void |
e6ccf245 | 1277 | storeSetMemStatus(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 | 1308 | const char * |
1309 | storeUrl(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 | |
1319 | void | |
1320 | storeCreateMemObject(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 */ |
1328 | void | |
8daca701 | 1329 | storeBuffer(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 */ | |
1335 | void | |
8daca701 | 1336 | storeBufferFlush(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 | 1343 | ssize_t |
07304bf9 | 1344 | objectLen(const StoreEntry * e) |
1345 | { | |
1346 | assert(e->mem_obj != NULL); | |
1347 | return e->mem_obj->object_sz; | |
1348 | } | |
1349 | ||
1350 | int | |
1351 | contentLen(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 | |
1358 | HttpReply * | |
1359 | storeEntryReply(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 | |
1368 | void | |
1369 | storeEntryReset(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 | */ | |
1388 | void | |
1389 | storeFsInit(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 | */ | |
1400 | void | |
1401 | storeFsDone(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 | 1414 | void |
a2c963ae | 1415 | storeFsAdd(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 | */ | |
1433 | void | |
a2c963ae | 1434 | storeReplAdd(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 | */ | |
1451 | RemovalPolicy * | |
1452 | createRemovalPolicy(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 | 1467 | void |
1468 | storeSwapFileNumberSet(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 | ||
1488 | NullStoreEntry NullStoreEntry::_instance; | |
1489 | ||
1490 | NullStoreEntry * | |
1491 | NullStoreEntry::getInstance() | |
1492 | { | |
1493 | return &_instance; | |
1494 | } |