]>
Commit | Line | Data |
---|---|---|
ff5731b1 | 1 | |
30a4f2a8 | 2 | /* |
38f903c4 | 3 | * $Id: store.cc,v 1.259 1997/06/18 16:15:53 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 20 Storeage Manager | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
c943f331 | 31 | |
32 | /* | |
30a4f2a8 | 33 | * Copyright (c) 1994, 1995. All rights reserved. |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
c943f331 | 105 | */ |
090089c4 | 106 | |
107 | /* | |
108 | * Here is a summary of the routines which change mem_status and swap_status: | |
109 | * Added 11/18/95 | |
110 | * | |
111 | * Routine mem_status swap_status status | |
112 | * --------------------------------------------------------------------------- | |
6eb42cae | 113 | * storeCreateEntry NOT_IN_MEMORY NO_SWAP |
090089c4 | 114 | * storeComplete IN_MEMORY NO_SWAP |
115 | * storeSwapOutStart SWAPPING_OUT | |
116 | * storeSwapOutHandle(fail) NO_SWAP | |
117 | * storeSwapOutHandle(ok) SWAP_OK | |
118 | * --------------------------------------------------------------------------- | |
119 | * storeAddDiskRestore NOT_IN_MEMORY SWAP_OK | |
120 | * storeSwapInStart SWAPPING_IN | |
121 | * storeSwapInHandle(fail) NOT_IN_MEMORY | |
122 | * storeSwapInHandle(ok) IN_MEMORY | |
123 | * --------------------------------------------------------------------------- | |
124 | * storeAbort IN_MEMORY NO_SWAP | |
125 | * storePurgeMem NOT_IN_MEMORY | |
126 | * --------------------------------------------------------------------------- | |
127 | * You can reclaim an object's space if it's: | |
128 | * storeGetSwapSpace !SWAPPING_IN !SWAPPING_OUT !STORE_PENDING | |
129 | * | |
130 | */ | |
131 | ||
44a47c6e | 132 | #include "squid.h" /* goes first */ |
641941c0 | 133 | #include "filemap.h" |
134 | #include "store_dir.h" | |
090089c4 | 135 | |
136 | #define REBUILD_TIMESTAMP_DELTA_MAX 2 | |
090089c4 | 137 | #define SWAP_BUF DISK_PAGE_SIZE |
090089c4 | 138 | |
227fbb74 | 139 | #define WITH_MEMOBJ 1 |
140 | #define WITHOUT_MEMOBJ 0 | |
141 | ||
fcf5283d | 142 | #define STORE_IN_MEM_BUCKETS (229) |
090089c4 | 143 | |
2285407f | 144 | #define STORE_LOG_CREATE 0 |
145 | #define STORE_LOG_SWAPIN 1 | |
146 | #define STORE_LOG_SWAPOUT 2 | |
147 | #define STORE_LOG_RELEASE 3 | |
148 | ||
38792624 | 149 | #define ENTRY_INMEM_SIZE(X) ((X)->e_current_len - (X)->e_lowest_offset) |
150 | ||
147d3115 | 151 | static char *storeLogTags[] = |
152 | { | |
153 | "CREATE", | |
154 | "SWAPIN", | |
155 | "SWAPOUT", | |
156 | "RELEASE" | |
2285407f | 157 | }; |
158 | ||
0ee4272b | 159 | const char *memStatusStr[] = |
e62d2dea | 160 | { |
9dfb6c1c | 161 | "NOT_IN_MEMORY", |
162 | "SWAPPING_IN", | |
163 | "IN_MEMORY" | |
164 | }; | |
165 | ||
0ee4272b | 166 | const char *pingStatusStr[] = |
e62d2dea | 167 | { |
f17936ab | 168 | "PING_NONE", |
9dfb6c1c | 169 | "PING_WAITING", |
170 | "PING_TIMEOUT", | |
f17936ab | 171 | "PING_DONE" |
9dfb6c1c | 172 | }; |
173 | ||
0ee4272b | 174 | const char *storeStatusStr[] = |
e62d2dea | 175 | { |
9dfb6c1c | 176 | "STORE_OK", |
177 | "STORE_PENDING", | |
178 | "STORE_ABORTED" | |
179 | }; | |
180 | ||
0ee4272b | 181 | const char *swapStatusStr[] = |
e62d2dea | 182 | { |
9dfb6c1c | 183 | "NO_SWAP", |
184 | "SWAPPING_OUT", | |
185 | "SWAP_OK" | |
186 | }; | |
187 | ||
5608850b | 188 | struct storeRebuildState { |
189 | struct _rebuild_dir { | |
190 | int dirn; | |
3e347513 | 191 | FILE *log; |
5608850b | 192 | int speed; |
193 | int clean; | |
194 | struct _rebuild_dir *next; | |
3e347513 | 195 | } *rebuild_dir; |
3facc31e | 196 | int objcount; /* # objects successfully reloaded */ |
197 | int expcount; /* # objects expired */ | |
198 | int linecount; /* # lines parsed from cache logfile */ | |
199 | int clashcount; /* # swapfile clashes avoided */ | |
200 | int dupcount; /* # duplicates purged */ | |
f182d1c5 | 201 | int need_to_validate; |
5608850b | 202 | time_t start; |
203 | time_t stop; | |
204 | char *line_in; | |
205 | size_t line_in_sz; | |
3facc31e | 206 | }; |
207 | ||
d3c12084 | 208 | struct _bucketOrder { |
0c77d853 | 209 | unsigned int bucket; |
210 | int index; | |
d3c12084 | 211 | }; |
212 | ||
0a0bf5db | 213 | typedef struct storeCleanList { |
214 | char *key; | |
215 | struct storeCleanList *next; | |
216 | } storeCleanList; | |
217 | ||
35ac11a3 | 218 | typedef void (VCB) _PARAMS((void *)); |
0a0bf5db | 219 | |
220 | typedef struct valid_ctrl_t { | |
221 | struct stat *sb; | |
222 | StoreEntry *e; | |
d7b78fbb | 223 | VCB *callback; |
0a0bf5db | 224 | void *callback_data; |
225 | } valid_ctrl_t; | |
226 | ||
227 | typedef struct swapin_ctrl_t { | |
228 | StoreEntry *e; | |
229 | char *path; | |
582b6456 | 230 | SIH *callback; |
0a0bf5db | 231 | void *callback_data; |
232 | } swapin_ctrl_t; | |
233 | ||
234 | typedef struct lock_ctrl_t { | |
582b6456 | 235 | SIH *callback; |
0a0bf5db | 236 | void *callback_data; |
237 | StoreEntry *e; | |
238 | } lock_ctrl_t; | |
239 | ||
240 | typedef struct swapout_ctrl_t { | |
241 | char *swapfilename; | |
242 | int oldswapstatus; | |
243 | StoreEntry *e; | |
0a0bf5db | 244 | } swapout_ctrl_t; |
245 | ||
30a4f2a8 | 246 | /* initializtion flag */ |
311ea387 | 247 | int store_rebuilding = 1; |
30a4f2a8 | 248 | |
249 | /* Static Functions */ | |
76dd1215 | 250 | static void storeCreateHashTable _PARAMS((int (*)_PARAMS((const char *, const char *)))); |
24382924 | 251 | static int compareLastRef _PARAMS((StoreEntry **, StoreEntry **)); |
252 | static int compareSize _PARAMS((StoreEntry **, StoreEntry **)); | |
429fdbec | 253 | static int compareBucketOrder _PARAMS((struct _bucketOrder *, struct _bucketOrder *)); |
254 | static int storeCheckExpired _PARAMS((const StoreEntry *, int flag)); | |
0ee4272b | 255 | static int storeCheckPurgeMem _PARAMS((const StoreEntry *)); |
382d851a | 256 | static int storeClientListSearch _PARAMS((const MemObject *, void *)); |
0ee4272b | 257 | static int storeCopy _PARAMS((const StoreEntry *, int, int, char *, int *)); |
258 | static int storeEntryLocked _PARAMS((const StoreEntry *)); | |
259 | static int storeEntryValidLength _PARAMS((const StoreEntry *)); | |
38792624 | 260 | static void storeGetMemSpace _PARAMS((int)); |
24382924 | 261 | static int storeHashDelete _PARAMS((StoreEntry *)); |
0ee4272b | 262 | static int storeShouldPurgeMem _PARAMS((const StoreEntry *)); |
d89d1fb6 | 263 | static DRCB storeSwapInHandle; |
d7b78fbb | 264 | static VCB storeSwapInValidateComplete; |
0a0bf5db | 265 | static void storeSwapInStartComplete _PARAMS((void *, int)); |
24382924 | 266 | static int swapInError _PARAMS((int, StoreEntry *)); |
267 | static mem_ptr new_MemObjectData _PARAMS((void)); | |
268 | static MemObject *new_MemObject _PARAMS((void)); | |
269 | static StoreEntry *new_StoreEntry _PARAMS((int)); | |
0a0bf5db | 270 | static StoreEntry *storeAddDiskRestore _PARAMS((const char *, |
271 | int, | |
272 | int, | |
273 | time_t, | |
274 | time_t, | |
311ea387 | 275 | time_t, |
276 | int)); | |
24382924 | 277 | static StoreEntry *storeGetInMemFirst _PARAMS((void)); |
278 | static StoreEntry *storeGetInMemNext _PARAMS((void)); | |
279 | static unsigned int storeGetBucketNum _PARAMS((void)); | |
67508012 | 280 | static void destroy_MemObject _PARAMS((MemObject *)); |
281 | static void destroy_MemObjectData _PARAMS((MemObject *)); | |
282 | static void destroy_StoreEntry _PARAMS((StoreEntry *)); | |
24382924 | 283 | static void storeDeleteBehind _PARAMS((StoreEntry *)); |
284 | static void storePurgeMem _PARAMS((StoreEntry *)); | |
24382924 | 285 | static void storeSetMemStatus _PARAMS((StoreEntry *, mem_status_t)); |
286 | static void storeStartRebuildFromDisk _PARAMS((void)); | |
0a0bf5db | 287 | static void storeSwapOutStart _PARAMS((StoreEntry * e)); |
288 | static void storeSwapOutStartComplete _PARAMS((void *, int)); | |
d89d1fb6 | 289 | static DWCB storeSwapOutHandle; |
67508012 | 290 | static void storeHashMemInsert _PARAMS((StoreEntry *)); |
291 | static void storeHashMemDelete _PARAMS((StoreEntry *)); | |
fe54d06d | 292 | static void storeSetPrivateKey _PARAMS((StoreEntry *)); |
d7b78fbb | 293 | static EVH storeDoRebuildFromDisk; |
294 | static EVH storeCleanup; | |
295 | static VCB storeCleanupComplete; | |
296 | static void storeValidate _PARAMS((StoreEntry *, VCB *, void *)); | |
297 | static AIOCB storeValidateComplete; | |
5608850b | 298 | static void storeRebuiltFromDisk _PARAMS((struct storeRebuildState * data)); |
2cc3f720 | 299 | static unsigned int getKeyCounter _PARAMS((void)); |
429fdbec | 300 | static void storePutUnusedFileno _PARAMS((int fileno)); |
301 | static int storeGetUnusedFileno _PARAMS((void)); | |
30a4f2a8 | 302 | |
090089c4 | 303 | /* Now, this table is inaccessible to outsider. They have to use a method |
304 | * to access a value in internal storage data structure. */ | |
35ac11a3 | 305 | static hash_table *store_table = NULL; |
090089c4 | 306 | /* hash table for in-memory-only objects */ |
35ac11a3 | 307 | static hash_table *in_mem_table = NULL; |
090089c4 | 308 | |
090089c4 | 309 | /* current memory storage size */ |
e954773d | 310 | unsigned long store_mem_size = 0; |
311 | ||
13c72789 | 312 | static int store_pages_max = 0; |
e954773d | 313 | static int store_pages_high = 0; |
314 | static int store_pages_low = 0; | |
090089c4 | 315 | |
090089c4 | 316 | /* current file name, swap file, use number as a filename */ |
76fefb77 | 317 | int store_swap_size = 0; /* kilobytes !! */ |
58104eab | 318 | static int store_swap_high = 0; |
319 | static int store_swap_low = 0; | |
2285407f | 320 | static int storelog_fd = -1; |
090089c4 | 321 | |
090089c4 | 322 | /* key temp buffer */ |
ec74cf5f | 323 | static char key_temp_buffer[MAX_URL + 100]; |
090089c4 | 324 | |
66cedb85 | 325 | /* expiration parameters and stats */ |
326 | static int store_buckets; | |
e924600d | 327 | static int store_maintain_rate; |
66cedb85 | 328 | static int store_maintain_buckets; |
e924600d | 329 | static int scan_revolutions; |
d3c12084 | 330 | static struct _bucketOrder *MaintBucketsOrder = NULL; |
66cedb85 | 331 | |
b8d8561b | 332 | static MemObject * |
333 | new_MemObject(void) | |
227fbb74 | 334 | { |
30a4f2a8 | 335 | MemObject *mem = get_free_mem_obj(); |
336 | mem->reply = xcalloc(1, sizeof(struct _http_reply)); | |
33b589ff | 337 | mem->reply->date = -2; |
338 | mem->reply->expires = -2; | |
339 | mem->reply->last_modified = -2; | |
0a0bf5db | 340 | mem->request = NULL; |
579a45cd | 341 | meta_data.mem_obj_count++; |
30a4f2a8 | 342 | meta_data.misc += sizeof(struct _http_reply); |
a3d5953d | 343 | debug(20, 3) ("new_MemObject: returning %p\n", mem); |
30a4f2a8 | 344 | return mem; |
227fbb74 | 345 | } |
346 | ||
b8d8561b | 347 | static StoreEntry * |
348 | new_StoreEntry(int mem_obj_flag) | |
090089c4 | 349 | { |
350 | StoreEntry *e = NULL; | |
351 | ||
30a4f2a8 | 352 | e = xcalloc(1, sizeof(StoreEntry)); |
227fbb74 | 353 | meta_data.store_entries++; |
354 | if (mem_obj_flag) | |
355 | e->mem_obj = new_MemObject(); | |
a3d5953d | 356 | debug(20, 3) ("new_StoreEntry: returning %p\n", e); |
227fbb74 | 357 | return e; |
090089c4 | 358 | } |
359 | ||
b8d8561b | 360 | static void |
361 | destroy_MemObject(MemObject * mem) | |
090089c4 | 362 | { |
a3d5953d | 363 | debug(20, 3) ("destroy_MemObject: destroying %p\n", mem); |
30a4f2a8 | 364 | destroy_MemObjectData(mem); |
52d4522b | 365 | safe_free(mem->clients); |
30a4f2a8 | 366 | safe_free(mem->reply); |
367 | safe_free(mem->e_abort_msg); | |
368 | requestUnlink(mem->request); | |
369 | mem->request = NULL; | |
370 | put_free_mem_obj(mem); | |
579a45cd | 371 | meta_data.mem_obj_count--; |
30a4f2a8 | 372 | meta_data.misc -= sizeof(struct _http_reply); |
090089c4 | 373 | } |
374 | ||
b8d8561b | 375 | static void |
376 | destroy_StoreEntry(StoreEntry * e) | |
090089c4 | 377 | { |
a3d5953d | 378 | debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e); |
85b701ed | 379 | if (!e) { |
380 | debug_trap("destroy_StoreEntry: NULL Entry"); | |
381 | return; | |
382 | } | |
227fbb74 | 383 | if (e->mem_obj) |
384 | destroy_MemObject(e->mem_obj); | |
30a4f2a8 | 385 | if (e->url) { |
386 | meta_data.url_strings -= strlen(e->url); | |
387 | safe_free(e->url); | |
388 | } else { | |
a3d5953d | 389 | debug(20, 3) ("destroy_StoreEntry: WARNING: Entry without URL string!\n"); |
30a4f2a8 | 390 | } |
391 | if (BIT_TEST(e->flag, KEY_URL)) | |
392 | e->key = NULL; | |
393 | else | |
394 | safe_free(e->key); | |
227fbb74 | 395 | xfree(e); |
396 | meta_data.store_entries--; | |
090089c4 | 397 | } |
398 | ||
b8d8561b | 399 | static mem_ptr |
400 | new_MemObjectData(void) | |
227fbb74 | 401 | { |
a3d5953d | 402 | debug(20, 3) ("new_MemObjectData: calling memInit()\n"); |
579a45cd | 403 | meta_data.mem_data_count++; |
227fbb74 | 404 | return memInit(); |
405 | } | |
090089c4 | 406 | |
b8d8561b | 407 | static void |
408 | destroy_MemObjectData(MemObject * mem) | |
090089c4 | 409 | { |
a3d5953d | 410 | debug(20, 3) ("destroy_MemObjectData: destroying %p, %d bytes\n", |
e954773d | 411 | mem->data, mem->e_current_len); |
38792624 | 412 | store_mem_size -= ENTRY_INMEM_SIZE(mem); |
30a4f2a8 | 413 | if (mem->data) { |
d7b78fbb | 414 | memFree(mem->data); |
30a4f2a8 | 415 | mem->data = NULL; |
579a45cd | 416 | meta_data.mem_data_count--; |
090089c4 | 417 | } |
30a4f2a8 | 418 | mem->e_current_len = 0; |
090089c4 | 419 | } |
420 | ||
090089c4 | 421 | /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */ |
422 | ||
423 | /* | |
424 | * Create 2 hash tables, "table" has all objects, "in_mem_table" has only | |
425 | * objects in the memory. | |
426 | */ | |
427 | ||
76dd1215 | 428 | static void |
35ac11a3 | 429 | storeCreateHashTable(HASHCMP * cmp_func) |
090089c4 | 430 | { |
66cedb85 | 431 | store_table = hash_create(cmp_func, store_buckets, hash4); |
432 | in_mem_table = hash_create(cmp_func, STORE_IN_MEM_BUCKETS, hash4); | |
090089c4 | 433 | } |
434 | ||
b8d8561b | 435 | static void |
436 | storeHashMemInsert(StoreEntry * e) | |
b32508fb | 437 | { |
438 | hash_insert(in_mem_table, e->key, e); | |
439 | meta_data.hot_vm++; | |
440 | } | |
441 | ||
b8d8561b | 442 | static void |
443 | storeHashMemDelete(StoreEntry * e) | |
b32508fb | 444 | { |
445 | hash_link *hptr = hash_lookup(in_mem_table, e->key); | |
85b701ed | 446 | if (hptr == NULL) { |
447 | debug_trap("storeHashMemDelete: key not found"); | |
448 | return; | |
449 | } | |
b32508fb | 450 | hash_delete_link(in_mem_table, hptr); |
451 | meta_data.hot_vm--; | |
452 | } | |
090089c4 | 453 | |
b8d8561b | 454 | static int |
455 | storeHashInsert(StoreEntry * e) | |
090089c4 | 456 | { |
a3d5953d | 457 | debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n", |
227fbb74 | 458 | e, e->key); |
090089c4 | 459 | if (e->mem_status == IN_MEMORY) |
b32508fb | 460 | storeHashMemInsert(e); |
461 | return hash_join(store_table, (hash_link *) e); | |
090089c4 | 462 | } |
463 | ||
b8d8561b | 464 | static int |
465 | storeHashDelete(StoreEntry * e) | |
090089c4 | 466 | { |
b32508fb | 467 | if (e->mem_status == IN_MEMORY) |
468 | storeHashMemDelete(e); | |
469 | return hash_remove_link(store_table, (hash_link *) e); | |
090089c4 | 470 | } |
471 | ||
472 | /* | |
473 | * maintain the in-mem hash table according to the changes of mem_status | |
234967c9 | 474 | * This routine replaces the instruction "e->store_status = status;" |
090089c4 | 475 | */ |
24382924 | 476 | static void |
b8d8561b | 477 | storeSetMemStatus(StoreEntry * e, mem_status_t status) |
090089c4 | 478 | { |
85b701ed | 479 | if (e->key == NULL) { |
480 | debug_trap("storeSetMemStatus: NULL key"); | |
481 | return; | |
482 | } else if (status != IN_MEMORY && e->mem_status == IN_MEMORY) | |
b32508fb | 483 | storeHashMemDelete(e); |
484 | else if (status == IN_MEMORY && e->mem_status != IN_MEMORY) | |
485 | storeHashMemInsert(e); | |
090089c4 | 486 | e->mem_status = status; |
487 | } | |
488 | ||
489 | /* -------------------------------------------------------------------------- */ | |
490 | ||
b8d8561b | 491 | static void |
fe4e214f | 492 | storeLog(int tag, const StoreEntry * e) |
2285407f | 493 | { |
0be3ec60 | 494 | LOCAL_ARRAY(char, logmsg, MAX_URL << 1); |
ca98227c | 495 | MemObject *mem = e->mem_obj; |
496 | struct _http_reply *reply; | |
2285407f | 497 | if (storelog_fd < 0) |
498 | return; | |
ca98227c | 499 | if (mem == NULL) |
500 | return; | |
501 | reply = mem->reply; | |
2abe4822 | 502 | sprintf(logmsg, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n", |
2285407f | 503 | (int) current_time.tv_sec, |
504 | (int) current_time.tv_usec / 1000, | |
505 | storeLogTags[tag], | |
2abe4822 | 506 | e->swap_file_number, |
ca98227c | 507 | reply->code, |
508 | (int) reply->date, | |
509 | (int) reply->last_modified, | |
510 | (int) reply->expires, | |
511 | reply->content_type[0] ? reply->content_type : "unknown", | |
512 | reply->content_length, | |
513 | mem->e_current_len - mem->reply->hdr_sz, | |
514 | RequestMethodStr[e->method], | |
2285407f | 515 | e->key); |
516 | file_write(storelog_fd, | |
517 | xstrdup(logmsg), | |
518 | strlen(logmsg), | |
2285407f | 519 | NULL, |
e47afcdb | 520 | NULL, |
521 | xfree); | |
2285407f | 522 | } |
523 | ||
090089c4 | 524 | |
525 | /* get rid of memory copy of the object */ | |
620da955 | 526 | /* Only call this if storeCheckPurgeMem(e) returns 1 */ |
24382924 | 527 | static void |
b8d8561b | 528 | storePurgeMem(StoreEntry * e) |
090089c4 | 529 | { |
a3d5953d | 530 | debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n", e->key); |
2aca8433 | 531 | if (e->mem_obj == NULL) |
090089c4 | 532 | return; |
090089c4 | 533 | storeSetMemStatus(e, NOT_IN_MEMORY); |
227fbb74 | 534 | destroy_MemObject(e->mem_obj); |
535 | e->mem_obj = NULL; | |
090089c4 | 536 | } |
537 | ||
0a0bf5db | 538 | void |
95c4b18f | 539 | storeLockObject(StoreEntry * e) |
090089c4 | 540 | { |
090089c4 | 541 | e->lock_count++; |
a3d5953d | 542 | debug(20, 3) ("storeLockObject: key '%s' count=%d\n", |
148a2921 | 543 | e->key, (int) e->lock_count); |
b8de7ebe | 544 | e->lastref = squid_curtime; |
090089c4 | 545 | } |
546 | ||
b8d8561b | 547 | void |
548 | storeReleaseRequest(StoreEntry * e) | |
2285407f | 549 | { |
aadcfbad | 550 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
58bcd31b | 551 | return; |
9e4ad609 | 552 | if (!storeEntryLocked(e)) |
553 | fatal_dump("storeReleaseRequest: unlocked entry"); | |
a3d5953d | 554 | debug(20, 3) ("storeReleaseRequest: '%s'\n", e->key); |
9e4ad609 | 555 | BIT_SET(e->flag, RELEASE_REQUEST); |
fe54d06d | 556 | storeSetPrivateKey(e); |
2285407f | 557 | } |
558 | ||
090089c4 | 559 | /* unlock object, return -1 if object get released after unlock |
560 | * otherwise lock_count */ | |
b8d8561b | 561 | int |
562 | storeUnlockObject(StoreEntry * e) | |
090089c4 | 563 | { |
6c895381 | 564 | MemObject *mem = e->mem_obj; |
565 | e->lock_count--; | |
a3d5953d | 566 | debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n", |
6c895381 | 567 | e->key, e->lock_count); |
30a4f2a8 | 568 | if (e->lock_count) |
a1e47288 | 569 | return (int) e->lock_count; |
5b00be7a | 570 | if (e->store_status == STORE_PENDING) { |
f182d1c5 | 571 | assert(!BIT_TEST(e->flag, ENTRY_DISPATCHED)); |
572 | BIT_SET(e->flag, RELEASE_REQUEST); | |
5b00be7a | 573 | } |
429fdbec | 574 | if (storePendingNClients(e) > 0) |
575 | debug_trap("storeUnlockObject: unlocked entry with pending clients\n"); | |
aadcfbad | 576 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) { |
30a4f2a8 | 577 | storeRelease(e); |
aadcfbad | 578 | } else if (BIT_TEST(e->flag, ABORT_MSG_PENDING)) { |
30a4f2a8 | 579 | /* This is where the negative cache gets storeAppended */ |
580 | /* Briefly lock to replace content with abort message */ | |
581 | e->lock_count++; | |
6c895381 | 582 | destroy_MemObjectData(mem); |
30a4f2a8 | 583 | e->object_len = 0; |
6c895381 | 584 | mem->data = new_MemObjectData(); |
585 | storeAppend(e, mem->e_abort_msg, strlen(mem->e_abort_msg)); | |
586 | e->object_len = mem->e_current_len = strlen(mem->e_abort_msg); | |
30a4f2a8 | 587 | BIT_RESET(e->flag, ABORT_MSG_PENDING); |
588 | e->lock_count--; | |
56f29785 | 589 | } else if (storeShouldPurgeMem(e)) { |
30a4f2a8 | 590 | storePurgeMem(e); |
090089c4 | 591 | } |
6c895381 | 592 | return 0; |
090089c4 | 593 | } |
594 | ||
595 | /* Lookup an object in the cache. | |
596 | * return just a reference to object, don't start swapping in yet. */ | |
b8d8561b | 597 | StoreEntry * |
0ee4272b | 598 | storeGet(const char *url) |
090089c4 | 599 | { |
a3d5953d | 600 | debug(20, 3) ("storeGet: looking up %s\n", url); |
b15fe823 | 601 | return (StoreEntry *) hash_lookup(store_table, url); |
090089c4 | 602 | } |
603 | ||
b8d8561b | 604 | unsigned int |
605 | getKeyCounter(void) | |
04e8dbaa | 606 | { |
2d25dc1a | 607 | static unsigned int key_counter = 0; |
7cb386d0 | 608 | if (++key_counter == (1 << 24)) |
2cc3f720 | 609 | key_counter = 1; |
2d25dc1a | 610 | return key_counter; |
04e8dbaa | 611 | } |
612 | ||
2cc3f720 | 613 | unsigned int |
614 | storeReqnum(StoreEntry * entry, method_t method) | |
615 | { | |
69951dbc | 616 | unsigned int k; |
2cc3f720 | 617 | if (BIT_TEST(entry->flag, KEY_PRIVATE)) |
69951dbc | 618 | k = atoi(entry->key); |
619 | else | |
620 | k = getKeyCounter(); | |
2cc3f720 | 621 | if (method == METHOD_GET) |
69951dbc | 622 | return k; |
623 | return (method << 24) | k; | |
2cc3f720 | 624 | } |
625 | ||
0ee4272b | 626 | const char * |
627 | storeGeneratePrivateKey(const char *url, method_t method, int num) | |
6eb42cae | 628 | { |
6eb42cae | 629 | if (num == 0) |
04e8dbaa | 630 | num = getKeyCounter(); |
2cc3f720 | 631 | else if (num & 0xFF000000) { |
632 | method = (method_t) (num >> 24); | |
633 | num &= 0x00FFFFFF; | |
634 | } | |
a3d5953d | 635 | debug(20, 3) ("storeGeneratePrivateKey: '%s'\n", url); |
6eb42cae | 636 | key_temp_buffer[0] = '\0'; |
637 | sprintf(key_temp_buffer, "%d/%s/%s", | |
638 | num, | |
73ddce89 | 639 | RequestMethodStr[method], |
6eb42cae | 640 | url); |
641 | return key_temp_buffer; | |
642 | } | |
643 | ||
0ee4272b | 644 | const char * |
645 | storeGeneratePublicKey(const char *url, method_t method) | |
090089c4 | 646 | { |
a3d5953d | 647 | debug(20, 3) ("storeGeneratePublicKey: type=%d %s\n", method, url); |
73ddce89 | 648 | switch (method) { |
6eb42cae | 649 | case METHOD_GET: |
227fbb74 | 650 | return url; |
983061ed | 651 | /* NOTREACHED */ |
227fbb74 | 652 | break; |
6eb42cae | 653 | case METHOD_POST: |
30a4f2a8 | 654 | case METHOD_PUT: |
6eb42cae | 655 | case METHOD_HEAD: |
3facc31e | 656 | case METHOD_CONNECT: |
b3b64e58 | 657 | case METHOD_TRACE: |
2cc3f720 | 658 | sprintf(key_temp_buffer, "/%s/%s", RequestMethodStr[method], url); |
b3b64e58 | 659 | return key_temp_buffer; |
660 | /* NOTREACHED */ | |
661 | break; | |
227fbb74 | 662 | default: |
85b701ed | 663 | debug_trap("storeGeneratePublicKey: Unsupported request method"); |
227fbb74 | 664 | break; |
665 | } | |
666 | return NULL; | |
667 | } | |
668 | ||
fe54d06d | 669 | static void |
b8d8561b | 670 | storeSetPrivateKey(StoreEntry * e) |
227fbb74 | 671 | { |
6eb42cae | 672 | hash_link *table_entry = NULL; |
0ee4272b | 673 | const char *newkey = NULL; |
6eb42cae | 674 | if (e->key && BIT_TEST(e->flag, KEY_PRIVATE)) |
675 | return; /* is already private */ | |
73ddce89 | 676 | newkey = storeGeneratePrivateKey(e->url, e->method, 0); |
ec74cf5f | 677 | if ((table_entry = hash_lookup(store_table, newkey))) { |
be0d70ce | 678 | debug_trap("storeSetPrivateKey: duplicate private key"); |
85b701ed | 679 | return; |
227fbb74 | 680 | } |
07622ccd | 681 | if (e->key) |
edae7bc0 | 682 | storeHashDelete(e); |
07622ccd | 683 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) |
684 | safe_free(e->key); | |
685 | e->key = xstrdup(newkey); | |
6eb42cae | 686 | storeHashInsert(e); |
227fbb74 | 687 | BIT_RESET(e->flag, KEY_URL); |
6eb42cae | 688 | BIT_SET(e->flag, KEY_CHANGE); |
689 | BIT_SET(e->flag, KEY_PRIVATE); | |
227fbb74 | 690 | } |
691 | ||
b8d8561b | 692 | void |
693 | storeSetPublicKey(StoreEntry * e) | |
227fbb74 | 694 | { |
6eb42cae | 695 | StoreEntry *e2 = NULL; |
696 | hash_link *table_entry = NULL; | |
0ee4272b | 697 | const char *newkey = NULL; |
234967c9 | 698 | int loop_detect = 0; |
6eb42cae | 699 | |
700 | if (e->key && !BIT_TEST(e->flag, KEY_PRIVATE)) | |
701 | return; /* is already public */ | |
702 | ||
73ddce89 | 703 | newkey = storeGeneratePublicKey(e->url, e->method); |
ec74cf5f | 704 | while ((table_entry = hash_lookup(store_table, newkey))) { |
a3d5953d | 705 | debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", newkey); |
07622ccd | 706 | e2 = (StoreEntry *) table_entry; |
707 | storeSetPrivateKey(e2); | |
30a4f2a8 | 708 | storeRelease(e2); |
234967c9 | 709 | if (loop_detect++ == 10) |
710 | fatal_dump("storeSetPublicKey() is looping!!"); | |
711 | newkey = storeGeneratePublicKey(e->url, e->method); | |
6eb42cae | 712 | } |
07622ccd | 713 | if (e->key) |
edae7bc0 | 714 | storeHashDelete(e); |
07622ccd | 715 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) |
716 | safe_free(e->key); | |
73ddce89 | 717 | if (e->method == METHOD_GET) { |
6eb42cae | 718 | e->key = e->url; |
719 | BIT_SET(e->flag, KEY_URL); | |
720 | BIT_RESET(e->flag, KEY_CHANGE); | |
721 | } else { | |
07622ccd | 722 | e->key = xstrdup(newkey); |
6eb42cae | 723 | BIT_RESET(e->flag, KEY_URL); |
724 | BIT_SET(e->flag, KEY_CHANGE); | |
227fbb74 | 725 | } |
07622ccd | 726 | BIT_RESET(e->flag, KEY_PRIVATE); |
227fbb74 | 727 | storeHashInsert(e); |
728 | } | |
729 | ||
b8d8561b | 730 | StoreEntry * |
62dec5a7 | 731 | storeCreateEntry(const char *url, int flags, method_t method) |
090089c4 | 732 | { |
090089c4 | 733 | StoreEntry *e = NULL; |
30a4f2a8 | 734 | MemObject *mem = NULL; |
a3d5953d | 735 | debug(20, 3) ("storeCreateEntry: '%s' icp flags=%x\n", url, flags); |
090089c4 | 736 | |
6eb42cae | 737 | e = new_StoreEntry(WITH_MEMOBJ); |
30a4f2a8 | 738 | e->lock_count = 1; /* Note lock here w/o calling storeLock() */ |
739 | mem = e->mem_obj; | |
090089c4 | 740 | e->url = xstrdup(url); |
6eb42cae | 741 | meta_data.url_strings += strlen(url); |
73ddce89 | 742 | e->method = method; |
234967c9 | 743 | if (BIT_TEST(flags, REQ_CACHABLE)) { |
1c481e00 | 744 | BIT_SET(e->flag, ENTRY_CACHABLE); |
090089c4 | 745 | BIT_RESET(e->flag, RELEASE_REQUEST); |
746 | } else { | |
1c481e00 | 747 | BIT_RESET(e->flag, ENTRY_CACHABLE); |
2daae136 | 748 | storeReleaseRequest(e); |
090089c4 | 749 | } |
234967c9 | 750 | if (BIT_TEST(flags, REQ_HIERARCHICAL)) |
751 | BIT_SET(e->flag, HIERARCHICAL); | |
752 | else | |
753 | BIT_RESET(e->flag, HIERARCHICAL); | |
754 | if (neighbors_do_private_keys || !BIT_TEST(flags, REQ_HIERARCHICAL)) | |
6eb42cae | 755 | storeSetPrivateKey(e); |
090089c4 | 756 | else |
6eb42cae | 757 | storeSetPublicKey(e); |
caebbe00 | 758 | BIT_SET(e->flag, ENTRY_HTML); |
090089c4 | 759 | |
234967c9 | 760 | e->store_status = STORE_PENDING; |
090089c4 | 761 | storeSetMemStatus(e, NOT_IN_MEMORY); |
762 | e->swap_status = NO_SWAP; | |
763 | e->swap_file_number = -1; | |
30a4f2a8 | 764 | mem->data = new_MemObjectData(); |
090089c4 | 765 | e->refcount = 0; |
b8de7ebe | 766 | e->lastref = squid_curtime; |
ca98227c | 767 | e->timestamp = 0; /* set in storeTimestampsSet() */ |
30a4f2a8 | 768 | e->ping_status = PING_NONE; |
0a0bf5db | 769 | BIT_SET(e->flag, ENTRY_VALIDATED); |
227fbb74 | 770 | |
090089c4 | 771 | /* allocate client list */ |
52d4522b | 772 | mem->nclients = MIN_CLIENT; |
d3a743b1 | 773 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
2285407f | 774 | /* storeLog(STORE_LOG_CREATE, e); */ |
090089c4 | 775 | return e; |
776 | } | |
777 | ||
778 | /* Add a new object to the cache with empty memory copy and pointer to disk | |
779 | * use to rebuild store from disk. */ | |
24382924 | 780 | static StoreEntry * |
311ea387 | 781 | storeAddDiskRestore(const char *url, int file_number, int size, time_t expires, time_t timestamp, time_t lastmod, int clean) |
090089c4 | 782 | { |
783 | StoreEntry *e = NULL; | |
784 | ||
a3d5953d | 785 | debug(20, 5) ("StoreAddDiskRestore: '%s': size %d: expires %d: fileno=%08X\n", |
090089c4 | 786 | url, size, expires, file_number); |
787 | ||
3facc31e | 788 | /* if you call this you'd better be sure file_number is not |
789 | * already in use! */ | |
66a91d2d | 790 | |
090089c4 | 791 | meta_data.url_strings += strlen(url); |
792 | ||
227fbb74 | 793 | e = new_StoreEntry(WITHOUT_MEMOBJ); |
090089c4 | 794 | e->url = xstrdup(url); |
73ddce89 | 795 | e->method = METHOD_GET; |
6eb42cae | 796 | storeSetPublicKey(e); |
1c481e00 | 797 | BIT_SET(e->flag, ENTRY_CACHABLE); |
090089c4 | 798 | BIT_RESET(e->flag, RELEASE_REQUEST); |
6eb42cae | 799 | BIT_SET(e->flag, ENTRY_HTML); |
234967c9 | 800 | e->store_status = STORE_OK; |
090089c4 | 801 | storeSetMemStatus(e, NOT_IN_MEMORY); |
802 | e->swap_status = SWAP_OK; | |
803 | e->swap_file_number = file_number; | |
090089c4 | 804 | e->object_len = size; |
805 | e->lock_count = 0; | |
090089c4 | 806 | e->refcount = 0; |
3c0117c9 | 807 | e->lastref = timestamp; |
f5469bce | 808 | e->timestamp = timestamp; |
809 | e->expires = expires; | |
810 | e->lastmod = lastmod; | |
30a4f2a8 | 811 | e->ping_status = PING_NONE; |
311ea387 | 812 | if (clean) { |
3e347513 | 813 | BIT_SET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 814 | /* Only set the file bit if we know its a valid entry */ |
815 | /* otherwise, set it in the validation procedure */ | |
816 | storeDirMapBitSet(file_number); | |
817 | } else { | |
3e347513 | 818 | BIT_RESET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 819 | } |
090089c4 | 820 | return e; |
821 | } | |
822 | ||
b8d8561b | 823 | int |
382d851a | 824 | storeUnregister(StoreEntry * e, void *data) |
090089c4 | 825 | { |
826 | int i; | |
6e40f263 | 827 | MemObject *mem = e->mem_obj; |
f990cccc | 828 | struct _store_client *sc; |
38792624 | 829 | if (mem == NULL) |
830 | return 0; | |
a3d5953d | 831 | debug(20, 3) ("storeUnregister: called for '%s'\n", e->key); |
382d851a | 832 | if ((i = storeClientListSearch(mem, data)) < 0) |
52d4522b | 833 | return 0; |
f990cccc | 834 | sc = &mem->clients[i]; |
fe96bbe6 | 835 | sc->seen_offset = 0; |
836 | sc->copy_offset = 0; | |
f990cccc | 837 | sc->callback = NULL; |
838 | sc->callback_data = NULL; | |
a3d5953d | 839 | debug(20, 9) ("storeUnregister: returning 1\n"); |
52d4522b | 840 | return 1; |
090089c4 | 841 | } |
842 | ||
b8d8561b | 843 | int |
fe4e214f | 844 | storeGetLowestReaderOffset(const StoreEntry * entry) |
234967c9 | 845 | { |
0ee4272b | 846 | const MemObject *mem = entry->mem_obj; |
30a4f2a8 | 847 | int lowest = mem->e_current_len; |
234967c9 | 848 | int i; |
52d4522b | 849 | for (i = 0; i < mem->nclients; i++) { |
382d851a | 850 | if (mem->clients[i].callback_data == NULL) |
234967c9 | 851 | continue; |
fe96bbe6 | 852 | if (mem->clients[i].copy_offset < lowest) |
853 | lowest = mem->clients[i].copy_offset; | |
234967c9 | 854 | } |
855 | return lowest; | |
856 | } | |
857 | ||
090089c4 | 858 | /* Call to delete behind upto "target lowest offset" |
234967c9 | 859 | * also, update e_lowest_offset */ |
24382924 | 860 | static void |
b8d8561b | 861 | storeDeleteBehind(StoreEntry * e) |
090089c4 | 862 | { |
30a4f2a8 | 863 | MemObject *mem = e->mem_obj; |
38792624 | 864 | int old_lowest_offset = mem->e_lowest_offset; |
865 | int new_lowest_offset; | |
866 | int target_offset = storeGetLowestReaderOffset(e); | |
867 | if (target_offset == 0) | |
868 | return; | |
38f903c4 | 869 | new_lowest_offset = (int) memFreeDataUpto(mem->data, target_offset); |
38792624 | 870 | store_mem_size -= new_lowest_offset - old_lowest_offset; |
871 | mem->e_lowest_offset = new_lowest_offset; | |
090089c4 | 872 | } |
873 | ||
874 | /* Call handlers waiting for data to be appended to E. */ | |
498d87ae | 875 | void |
b8d8561b | 876 | InvokeHandlers(StoreEntry * e) |
090089c4 | 877 | { |
878 | int i; | |
9dfb6c1c | 879 | MemObject *mem = e->mem_obj; |
d89d1fb6 | 880 | STCB *callback = NULL; |
52d4522b | 881 | struct _store_client *sc; |
d89d1fb6 | 882 | size_t size; |
1bab7e0b | 883 | if (mem->clients == NULL && mem->nclients) { |
28070024 | 884 | debug_trap("InvokeHandlers: NULL mem->clients"); |
885 | return; | |
886 | } | |
d89d1fb6 | 887 | /* walk the entire list looking for valid callbacks */ |
52d4522b | 888 | for (i = 0; i < mem->nclients; i++) { |
889 | sc = &mem->clients[i]; | |
382d851a | 890 | if (sc->callback_data == NULL) |
63cc9284 | 891 | continue; |
d89d1fb6 | 892 | if ((callback = sc->callback) == NULL) |
63cc9284 | 893 | continue; |
52d4522b | 894 | sc->callback = NULL; |
382d851a | 895 | /* Don't NULL the callback_data, its used to identify the client */ |
d89d1fb6 | 896 | size = memCopy(mem->data, |
fe96bbe6 | 897 | sc->copy_offset, |
d89d1fb6 | 898 | sc->copy_buf, |
899 | sc->copy_size); | |
900 | callback(sc->callback_data, sc->copy_buf, size); | |
090089c4 | 901 | } |
090089c4 | 902 | } |
903 | ||
6eb42cae | 904 | /* Mark object as expired */ |
b8d8561b | 905 | void |
906 | storeExpireNow(StoreEntry * e) | |
9174e204 | 907 | { |
a3d5953d | 908 | debug(20, 3) ("storeExpireNow: '%s'\n", e->key); |
b8de7ebe | 909 | e->expires = squid_curtime; |
9174e204 | 910 | } |
911 | ||
6eb42cae | 912 | /* switch object to deleting behind mode call by |
913 | * retrieval module when object gets too big. */ | |
b8d8561b | 914 | void |
915 | storeStartDeleteBehind(StoreEntry * e) | |
090089c4 | 916 | { |
aadcfbad | 917 | if (BIT_TEST(e->flag, DELETE_BEHIND)) |
090089c4 | 918 | return; |
a3d5953d | 919 | debug(20, e->mem_obj->e_current_len ? 1 : 3) |
920 | ("storeStartDeleteBehind: '%s' at %d bytes\n", | |
48f44632 | 921 | e->url, e->mem_obj->e_current_len); |
6eb42cae | 922 | storeSetPrivateKey(e); |
090089c4 | 923 | BIT_SET(e->flag, DELETE_BEHIND); |
2daae136 | 924 | storeReleaseRequest(e); |
1c481e00 | 925 | BIT_RESET(e->flag, ENTRY_CACHABLE); |
9174e204 | 926 | storeExpireNow(e); |
090089c4 | 927 | } |
928 | ||
929 | /* Append incoming data from a primary server to an entry. */ | |
b8d8561b | 930 | void |
3a1c3e2f | 931 | storeAppend(StoreEntry * e, const char *buf, int len) |
090089c4 | 932 | { |
3a1c3e2f | 933 | MemObject *mem = e->mem_obj; |
934 | assert(mem != NULL); | |
935 | assert(len >= 0); | |
090089c4 | 936 | if (len) { |
a3d5953d | 937 | debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", len, e->key); |
38792624 | 938 | storeGetMemSpace(len); |
450e6053 | 939 | if (sm_stats.n_pages_in_use > store_pages_low) { |
38792624 | 940 | if (mem->e_current_len > Config.Store.maxObjectSize) |
941 | storeStartDeleteBehind(e); | |
942 | } | |
090089c4 | 943 | store_mem_size += len; |
3a1c3e2f | 944 | memAppend(mem->data, buf, len); |
38792624 | 945 | mem->e_current_len += len; |
090089c4 | 946 | } |
aadcfbad | 947 | if (e->store_status != STORE_ABORTED && !BIT_TEST(e->flag, DELAY_SENDING)) |
090089c4 | 948 | InvokeHandlers(e); |
090089c4 | 949 | } |
950 | ||
24382924 | 951 | #ifdef __STDC__ |
b8d8561b | 952 | void |
fe4e214f | 953 | storeAppendPrintf(StoreEntry * e, const char *fmt,...) |
c30c5a73 | 954 | { |
15c05bb0 | 955 | va_list args; |
95d659f0 | 956 | LOCAL_ARRAY(char, buf, 4096); |
15c05bb0 | 957 | va_start(args, fmt); |
c30c5a73 | 958 | #else |
b8d8561b | 959 | void |
960 | storeAppendPrintf(va_alist) | |
15c05bb0 | 961 | va_dcl |
962 | { | |
963 | va_list args; | |
964 | StoreEntry *e = NULL; | |
0ee4272b | 965 | const char *fmt = NULL; |
95d659f0 | 966 | LOCAL_ARRAY(char, buf, 4096); |
15c05bb0 | 967 | va_start(args); |
968 | e = va_arg(args, StoreEntry *); | |
969 | fmt = va_arg(args, char *); | |
c30c5a73 | 970 | #endif |
15c05bb0 | 971 | buf[0] = '\0'; |
972 | vsprintf(buf, fmt, args); | |
973 | storeAppend(e, buf, strlen(buf)); | |
974 | va_end(args); | |
c30c5a73 | 975 | } |
976 | ||
090089c4 | 977 | /* swapping in handle */ |
582b6456 | 978 | static void |
979 | storeSwapInHandle(int u1, const char *buf, int len, int flag, void *data) | |
090089c4 | 980 | { |
582b6456 | 981 | StoreEntry *e = data; |
30a4f2a8 | 982 | MemObject *mem = e->mem_obj; |
311ea387 | 983 | assert(mem); |
a3d5953d | 984 | debug(20, 2) ("storeSwapInHandle: '%s'\n", e->key); |
090089c4 | 985 | if ((flag < 0) && (flag != DISK_EOF)) { |
a3d5953d | 986 | debug(20, 0) ("storeSwapInHandle: SwapIn failure (err code = %d).\n", flag); |
30a4f2a8 | 987 | put_free_8k_page(mem->e_swap_buf); |
090089c4 | 988 | storeSetMemStatus(e, NOT_IN_MEMORY); |
e47afcdb | 989 | file_close(mem->swapin_fd); |
090089c4 | 990 | swapInError(-1, e); /* Invokes storeAbort() and completes the I/O */ |
582b6456 | 991 | return; |
090089c4 | 992 | } |
a3d5953d | 993 | debug(20, 5) ("storeSwapInHandle: e->swap_offset = %d\n", mem->swap_offset); |
994 | debug(20, 5) ("storeSwapInHandle: e->e_current_len = %d\n", mem->e_current_len); | |
995 | debug(20, 5) ("storeSwapInHandle: e->object_len = %d\n", e->object_len); | |
ca98227c | 996 | if (len && mem->swap_offset == 0) |
997 | httpParseReplyHeaders(buf, mem->reply); | |
998 | /* Assumes we got all the headers in one read() */ | |
090089c4 | 999 | /* always call these, even if len == 0 */ |
30a4f2a8 | 1000 | mem->swap_offset += len; |
090089c4 | 1001 | storeAppend(e, buf, len); |
30a4f2a8 | 1002 | if (mem->e_current_len < e->object_len && flag != DISK_EOF) { |
090089c4 | 1003 | /* some more data to swap in, reschedule */ |
e47afcdb | 1004 | file_read(mem->swapin_fd, |
30a4f2a8 | 1005 | mem->e_swap_buf, |
090089c4 | 1006 | SWAP_BUF, |
30a4f2a8 | 1007 | mem->swap_offset, |
cd1fb0eb | 1008 | storeSwapInHandle, |
1009 | e); | |
582b6456 | 1010 | return; |
977a52ae | 1011 | } |
9e4ad609 | 1012 | if (mem->e_current_len > e->object_len) |
1013 | debug_trap("storeSwapInHandle: Too much data read!"); | |
977a52ae | 1014 | /* complete swapping in */ |
1015 | storeSetMemStatus(e, IN_MEMORY); | |
1016 | put_free_8k_page(mem->e_swap_buf); | |
1017 | file_close(mem->swapin_fd); | |
1018 | storeLog(STORE_LOG_SWAPIN, e); | |
a3d5953d | 1019 | debug(20, 5) ("storeSwapInHandle: SwapIn complete: '%s' from %s.\n", |
977a52ae | 1020 | e->url, storeSwapFullPath(e->swap_file_number, NULL)); |
1021 | if (mem->e_current_len != e->object_len) { | |
9e4ad609 | 1022 | debug_trap("storeSwapInHandle: Object size mismatch"); |
a3d5953d | 1023 | debug(20, 0) (" --> '%s'\n", e->url); |
1024 | debug(20, 0) (" --> Expecting %d bytes from file: %s\n", e->object_len, | |
977a52ae | 1025 | storeSwapFullPath(e->swap_file_number, NULL)); |
a3d5953d | 1026 | debug(20, 0) (" --> Only read %d bytes\n", |
977a52ae | 1027 | mem->e_current_len); |
1028 | } | |
1029 | e->lock_count++; /* lock while calling handler */ | |
1030 | InvokeHandlers(e); /* once more after mem_status state change */ | |
977a52ae | 1031 | e->lock_count--; |
1032 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) { | |
1033 | storeRelease(e); | |
1034 | } else if ((mem = e->mem_obj)) { | |
1035 | requestUnlink(mem->request); | |
1036 | mem->request = NULL; | |
090089c4 | 1037 | } |
090089c4 | 1038 | } |
1039 | ||
1040 | /* start swapping in */ | |
95c4b18f | 1041 | void |
582b6456 | 1042 | storeSwapInStart(StoreEntry * e, SIH * callback, void *callback_data) |
090089c4 | 1043 | { |
0a0bf5db | 1044 | swapin_ctrl_t *ctrlp; |
311ea387 | 1045 | if (e->mem_status != NOT_IN_MEMORY) { |
1046 | callback(callback_data, 0); | |
1047 | return; | |
1048 | } | |
1049 | if (e->store_status == STORE_PENDING) { | |
2abe4822 | 1050 | callback(callback_data, 0); |
1051 | return; | |
1052 | } | |
311ea387 | 1053 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) { |
97a88399 | 1054 | if (storeDirMapBitTest(e->swap_file_number)) { |
311ea387 | 1055 | /* someone took our file while we weren't looking */ |
1056 | callback(callback_data, -1); | |
1057 | return; | |
1058 | } | |
1059 | } | |
95c4b18f | 1060 | assert(e->swap_status == SWAP_OK); |
1061 | assert(e->swap_file_number >= 0); | |
1062 | assert(e->mem_obj == NULL); | |
0379374a | 1063 | e->mem_obj = new_MemObject(); |
0a0bf5db | 1064 | ctrlp = xmalloc(sizeof(swapin_ctrl_t)); |
1065 | ctrlp->e = e; | |
1066 | ctrlp->callback = callback; | |
1067 | ctrlp->callback_data = callback_data; | |
95c4b18f | 1068 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
35ac11a3 | 1069 | storeSwapInValidateComplete(ctrlp); |
95c4b18f | 1070 | else |
1071 | storeValidate(e, storeSwapInValidateComplete, ctrlp); | |
0a0bf5db | 1072 | } |
090089c4 | 1073 | |
0a0bf5db | 1074 | |
1075 | static void | |
35ac11a3 | 1076 | storeSwapInValidateComplete(void *data) |
0a0bf5db | 1077 | { |
1078 | swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data; | |
0a0bf5db | 1079 | StoreEntry *e; |
1080 | e = ctrlp->e; | |
95c4b18f | 1081 | assert(e->mem_status == NOT_IN_MEMORY); |
0a0bf5db | 1082 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) { |
fc07a0e4 | 1083 | /* Invoke a store abort that should free the memory object */ |
0a0bf5db | 1084 | (ctrlp->callback) (ctrlp->callback_data, -1); |
1085 | xfree(ctrlp); | |
1086 | return; | |
090089c4 | 1087 | } |
95c4b18f | 1088 | ctrlp->path = xstrdup(storeSwapFullPath(e->swap_file_number, NULL)); |
1089 | file_open(ctrlp->path, O_RDONLY, storeSwapInStartComplete, ctrlp); | |
0a0bf5db | 1090 | } |
1091 | ||
1092 | static void | |
1093 | storeSwapInStartComplete(void *data, int fd) | |
1094 | { | |
0a0bf5db | 1095 | swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data; |
1096 | StoreEntry *e = ctrlp->e; | |
0379374a | 1097 | MemObject *mem = e->mem_obj; |
1098 | assert(e->mem_obj != NULL); | |
95c4b18f | 1099 | assert(e->mem_status == NOT_IN_MEMORY); |
1100 | assert(e->swap_status == SWAP_OK); | |
0a0bf5db | 1101 | if (fd < 0) { |
a3d5953d | 1102 | debug(20, 0) ("storeSwapInStartComplete: Failed for '%s'\n", e->url); |
0a0bf5db | 1103 | /* Invoke a store abort that should free the memory object */ |
1104 | (ctrlp->callback) (ctrlp->callback_data, -1); | |
1105 | xfree(ctrlp->path); | |
1106 | xfree(ctrlp); | |
1107 | return; | |
1108 | } | |
0a0bf5db | 1109 | storeSetMemStatus(e, SWAPPING_IN); |
e47afcdb | 1110 | mem->swapin_fd = (short) fd; |
a3d5953d | 1111 | debug(20, 5) ("storeSwapInStart: initialized swap file '%s' for '%s'\n", |
0a0bf5db | 1112 | ctrlp->path, e->url); |
e47afcdb | 1113 | mem->data = new_MemObjectData(); |
1114 | mem->swap_offset = 0; | |
1115 | mem->e_swap_buf = get_free_8k_page(); | |
090089c4 | 1116 | /* start swapping daemon */ |
fc07a0e4 | 1117 | file_read(fd, |
e47afcdb | 1118 | mem->e_swap_buf, |
090089c4 | 1119 | SWAP_BUF, |
e47afcdb | 1120 | mem->swap_offset, |
cd1fb0eb | 1121 | storeSwapInHandle, |
1122 | e); | |
0a0bf5db | 1123 | (ctrlp->callback) (ctrlp->callback_data, 0); |
1124 | xfree(ctrlp->path); | |
1125 | xfree(ctrlp); | |
090089c4 | 1126 | } |
1127 | ||
24382924 | 1128 | static void |
d89d1fb6 | 1129 | storeSwapOutHandle(int fd, int flag, size_t len, void *data) |
090089c4 | 1130 | { |
582b6456 | 1131 | StoreEntry *e = data; |
e47afcdb | 1132 | MemObject *mem = e->mem_obj; |
a3d5953d | 1133 | debug(20, 3) ("storeSwapOutHandle: '%s'\n", e->key); |
be0d70ce | 1134 | if (mem == NULL) |
1135 | fatal_dump("storeSwapOutHandle: NULL mem_obj"); | |
090089c4 | 1136 | if (flag < 0) { |
a3d5953d | 1137 | debug(20, 1) ("storeSwapOutHandle: SwapOut failure (err code = %d).\n", |
090089c4 | 1138 | flag); |
1139 | e->swap_status = NO_SWAP; | |
e47afcdb | 1140 | put_free_8k_page(mem->e_swap_buf); |
090089c4 | 1141 | file_close(fd); |
090089c4 | 1142 | if (e->swap_file_number != -1) { |
429fdbec | 1143 | storePutUnusedFileno(e->swap_file_number); |
090089c4 | 1144 | e->swap_file_number = -1; |
1145 | } | |
b6a63185 | 1146 | storeRelease(e); |
090089c4 | 1147 | if (flag == DISK_NO_SPACE_LEFT) { |
1148 | /* reduce the swap_size limit to the current size. */ | |
b1c0cc67 | 1149 | Config.Swap.maxSize = store_swap_size; |
1150 | storeConfigure(); | |
090089c4 | 1151 | } |
1152 | return; | |
1153 | } | |
a3d5953d | 1154 | debug(20, 6) ("storeSwapOutHandle: e->swap_offset = %d\n", mem->swap_offset); |
1155 | debug(20, 6) ("storeSwapOutHandle: e->e_swap_buf_len = %d\n", mem->e_swap_buf_len); | |
1156 | debug(20, 6) ("storeSwapOutHandle: e->object_len = %d\n", e->object_len); | |
1157 | debug(20, 6) ("storeSwapOutHandle: store_swap_size = %dk\n", store_swap_size); | |
e47afcdb | 1158 | mem->swap_offset += mem->e_swap_buf_len; |
090089c4 | 1159 | /* round up */ |
c932b107 | 1160 | storeDirUpdateSwapSize(e->swap_file_number, mem->e_swap_buf_len, 1); |
e47afcdb | 1161 | if (mem->swap_offset >= e->object_len) { |
090089c4 | 1162 | /* swapping complete */ |
1163 | e->swap_status = SWAP_OK; | |
e47afcdb | 1164 | file_close(mem->swapout_fd); |
2285407f | 1165 | storeLog(STORE_LOG_SWAPOUT, e); |
a3d5953d | 1166 | debug(20, 5) ("storeSwapOutHandle: SwapOut complete: '%s' to %s.\n", |
090089c4 | 1167 | e->url, storeSwapFullPath(e->swap_file_number, NULL)); |
e47afcdb | 1168 | put_free_8k_page(mem->e_swap_buf); |
5608850b | 1169 | storeDirSwapLog(e); |
a528eb87 | 1170 | HTTPCacheInfo->proto_newobject(HTTPCacheInfo, |
e47afcdb | 1171 | mem->request->protocol, |
30a4f2a8 | 1172 | e->object_len, |
1173 | FALSE); | |
090089c4 | 1174 | /* check if it's request to be released. */ |
aadcfbad | 1175 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
090089c4 | 1176 | storeRelease(e); |
56f29785 | 1177 | else if (storeShouldPurgeMem(e)) |
30a4f2a8 | 1178 | storePurgeMem(e); |
12f6386d | 1179 | else { |
1180 | requestUnlink(mem->request); | |
1181 | mem->request = NULL; | |
1182 | } | |
090089c4 | 1183 | return; |
1184 | } | |
1185 | /* write some more data, reschedule itself. */ | |
0a0bf5db | 1186 | if (storeCopy(e, mem->swap_offset, SWAP_BUF, mem->e_swap_buf, &(mem->e_swap_buf_len)) < 0) { |
a3d5953d | 1187 | debug(20, 1) ("storeSwapOutHandle: SwapOut failure (err code = %d).\n", |
0a0bf5db | 1188 | flag); |
1189 | e->swap_status = NO_SWAP; | |
1190 | put_free_8k_page(mem->e_swap_buf); | |
1191 | file_close(fd); | |
1192 | if (e->swap_file_number != -1) { | |
641941c0 | 1193 | storeDirMapBitReset(e->swap_file_number); |
0a0bf5db | 1194 | safeunlink(storeSwapFullPath(e->swap_file_number, NULL), 0); |
1195 | e->swap_file_number = -1; | |
1196 | } | |
1197 | storeRelease(e); | |
1198 | return; | |
1199 | } | |
e47afcdb | 1200 | file_write(mem->swapout_fd, |
1201 | mem->e_swap_buf, | |
1202 | mem->e_swap_buf_len, | |
3facc31e | 1203 | storeSwapOutHandle, |
e47afcdb | 1204 | e, |
1205 | NULL); | |
090089c4 | 1206 | return; |
090089c4 | 1207 | } |
1208 | ||
090089c4 | 1209 | /* start swapping object to disk */ |
0a0bf5db | 1210 | static void |
b8d8561b | 1211 | storeSwapOutStart(StoreEntry * e) |
090089c4 | 1212 | { |
0a0bf5db | 1213 | swapout_ctrl_t *ctrlp; |
f2052513 | 1214 | LOCAL_ARRAY(char, swapfilename, SQUID_MAXPATHLEN); |
95c4b18f | 1215 | if ((e->swap_file_number = storeGetUnusedFileno()) < 0) |
1216 | e->swap_file_number = storeDirMapAllocate(); | |
1217 | storeSwapFullPath(e->swap_file_number, swapfilename); | |
0a0bf5db | 1218 | ctrlp = xmalloc(sizeof(swapout_ctrl_t)); |
1219 | ctrlp->swapfilename = xstrdup(swapfilename); | |
1220 | ctrlp->e = e; | |
1221 | ctrlp->oldswapstatus = e->swap_status; | |
0a0bf5db | 1222 | e->swap_status = SWAPPING_OUT; |
1223 | file_open(swapfilename, | |
0a0bf5db | 1224 | O_WRONLY | O_CREAT | O_TRUNC, |
1225 | storeSwapOutStartComplete, | |
1226 | ctrlp); | |
1227 | } | |
1228 | ||
1229 | static void | |
1230 | storeSwapOutStartComplete(void *data, int fd) | |
1231 | { | |
641941c0 | 1232 | swapout_ctrl_t *ctrlp = data; |
1233 | int oldswapstatus = ctrlp->oldswapstatus; | |
1234 | char *swapfilename = ctrlp->swapfilename; | |
1235 | StoreEntry *e = ctrlp->e; | |
0a0bf5db | 1236 | int x; |
0a0bf5db | 1237 | MemObject *mem; |
0a0bf5db | 1238 | xfree(ctrlp); |
090089c4 | 1239 | if (fd < 0) { |
a3d5953d | 1240 | debug(20, 0) ("storeSwapOutStart: Unable to open swapfile: %s\n", |
090089c4 | 1241 | swapfilename); |
95c4b18f | 1242 | storeDirMapBitReset(e->swap_file_number); |
090089c4 | 1243 | e->swap_file_number = -1; |
0a0bf5db | 1244 | if (e->swap_status == SWAPPING_OUT) |
1245 | e->swap_status = oldswapstatus; | |
1246 | xfree(swapfilename); | |
1247 | return; | |
090089c4 | 1248 | } |
0a0bf5db | 1249 | mem = e->mem_obj; |
e47afcdb | 1250 | mem->swapout_fd = (short) fd; |
a3d5953d | 1251 | debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to FD %d FILE %s.\n", |
090089c4 | 1252 | e->url, fd, swapfilename); |
a3d5953d | 1253 | debug(20, 5) ("swap_file_number=%08X\n", e->swap_file_number); |
090089c4 | 1254 | e->swap_status = SWAPPING_OUT; |
e47afcdb | 1255 | mem->swap_offset = 0; |
1256 | mem->e_swap_buf = get_free_8k_page(); | |
1257 | mem->e_swap_buf_len = 0; | |
0a0bf5db | 1258 | x = storeCopy(e, |
e47afcdb | 1259 | 0, |
1260 | SWAP_BUF, | |
1261 | mem->e_swap_buf, | |
a1e47288 | 1262 | &mem->e_swap_buf_len); |
0a0bf5db | 1263 | if (x < 0) { |
a3d5953d | 1264 | debug(20, 1) ("storeCopy returned %d for '%s'\n", x, e->key); |
0a0bf5db | 1265 | e->swap_file_number = -1; |
1266 | file_close(fd); | |
641941c0 | 1267 | storeDirMapBitReset(e->swap_file_number); |
0a0bf5db | 1268 | e->swap_file_number = -1; |
1269 | safeunlink(swapfilename, 1); | |
1270 | if (e->swap_status == SWAPPING_OUT) | |
1271 | e->swap_status = oldswapstatus; | |
1272 | xfree(swapfilename); | |
1273 | return; | |
1274 | } | |
090089c4 | 1275 | /* start swapping daemon */ |
e47afcdb | 1276 | x = file_write(mem->swapout_fd, |
1277 | mem->e_swap_buf, | |
1278 | mem->e_swap_buf_len, | |
e47afcdb | 1279 | storeSwapOutHandle, |
1280 | e, | |
1281 | NULL); | |
1282 | if (x != DISK_OK) | |
1283 | fatal_dump(NULL); /* This shouldn't happen */ | |
0a0bf5db | 1284 | xfree(swapfilename); |
090089c4 | 1285 | } |
1286 | ||
1287 | /* recreate meta data from disk image in swap directory */ | |
3facc31e | 1288 | /* Add one swap file at a time from disk storage */ |
48f44632 | 1289 | static void |
1290 | storeDoRebuildFromDisk(void *data) | |
3facc31e | 1291 | { |
5608850b | 1292 | struct storeRebuildState *RB = data; |
429fdbec | 1293 | LOCAL_ARRAY(char, swapfile, MAXPATHLEN); |
f2052513 | 1294 | LOCAL_ARRAY(char, url, MAX_URL); |
090089c4 | 1295 | StoreEntry *e = NULL; |
090089c4 | 1296 | time_t expires; |
1297 | time_t timestamp; | |
f5469bce | 1298 | time_t lastmod; |
1299 | int scan1; | |
1300 | int scan2; | |
1301 | int scan3; | |
1302 | int scan4; | |
3facc31e | 1303 | off_t size; |
090089c4 | 1304 | int sfileno = 0; |
30a4f2a8 | 1305 | int count; |
f5469bce | 1306 | int x; |
5608850b | 1307 | struct _rebuild_dir *d; |
1308 | struct _rebuild_dir **D; | |
429fdbec | 1309 | int used; /* is swapfile already in use? */ |
1310 | int newer; /* is the log entry newer than current entry? */ | |
1311 | ||
30a4f2a8 | 1312 | /* load a number of objects per invocation */ |
5608850b | 1313 | if ((d = RB->rebuild_dir) == NULL) { |
3e347513 | 1314 | storeRebuiltFromDisk(RB); |
1315 | return; | |
5608850b | 1316 | } |
1317 | for (count = 0; count < d->speed; count++) { | |
1318 | if (fgets(RB->line_in, RB->line_in_sz, d->log) == NULL) { | |
a3d5953d | 1319 | debug(20, 1) ("Done reading Cache Dir #%d swap log\n", d->dirn); |
3e347513 | 1320 | fclose(d->log); |
1321 | d->log = NULL; | |
1322 | storeDirCloseTmpSwapLog(d->dirn); | |
1323 | RB->rebuild_dir = d->next; | |
1324 | safe_free(d); | |
1325 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); | |
1326 | return; | |
48f44632 | 1327 | } |
5608850b | 1328 | if ((++RB->linecount & 0x3FFF) == 0) |
a3d5953d | 1329 | debug(20, 1) (" %7d Lines read so far.\n", RB->linecount); |
1330 | debug(20, 9) ("line_in: %s", RB->line_in); | |
5608850b | 1331 | if (RB->line_in[0] == '\0') |
1332 | continue; | |
1333 | if (RB->line_in[0] == '\n') | |
1334 | continue; | |
1335 | if (RB->line_in[0] == '#') | |
1336 | continue; | |
f5469bce | 1337 | url[0] = '\0'; |
f5469bce | 1338 | sfileno = 0; |
1339 | scan1 = 0; | |
1340 | scan2 = 0; | |
30a4f2a8 | 1341 | scan3 = 0; |
f5469bce | 1342 | scan4 = 0; |
5608850b | 1343 | x = sscanf(RB->line_in, "%x %x %x %x %d %s", |
0be3ec60 | 1344 | &sfileno, /* swap_file_number */ |
1345 | &scan1, /* timestamp */ | |
1346 | &scan2, /* expires */ | |
1347 | &scan3, /* last modified */ | |
1348 | &scan4, /* size */ | |
1349 | url); /* url */ | |
429fdbec | 1350 | if (x < 1) |
30a4f2a8 | 1351 | continue; |
429fdbec | 1352 | storeSwapFullPath(sfileno, swapfile); |
9e4ad609 | 1353 | if (x != 6) |
429fdbec | 1354 | continue; |
0a0bf5db | 1355 | if (sfileno < 0) |
1356 | continue; | |
c932b107 | 1357 | sfileno = storeDirProperFileno(d->dirn, sfileno); |
f5469bce | 1358 | timestamp = (time_t) scan1; |
1359 | expires = (time_t) scan2; | |
1360 | lastmod = (time_t) scan3; | |
1361 | size = (off_t) scan4; | |
30a4f2a8 | 1362 | |
429fdbec | 1363 | e = storeGet(url); |
1364 | used = storeDirMapBitTest(sfileno); | |
1365 | /* If this URL already exists in the cache, does the swap log | |
1366 | * appear to have a newer entry? Compare 'timestamp' from the | |
1367 | * swap log to e->lastref. Note, we can't compare e->timestamp | |
1368 | * because it is the Date: header from the HTTP reply and | |
1369 | * doesn't really tell us when the object was added to the | |
1370 | * cache. */ | |
1371 | newer = e ? (timestamp > e->lastref ? 1 : 0) : 0; | |
1372 | if (used && !newer) { | |
1373 | /* log entry is old, ignore it */ | |
1374 | RB->clashcount++; | |
1375 | continue; | |
1376 | } else if (used && e && e->swap_file_number == sfileno) { | |
1377 | /* swapfile taken, same URL, newer, update meta */ | |
1378 | e->lastref = timestamp; | |
1379 | e->timestamp = timestamp; | |
1380 | e->expires = expires; | |
1381 | e->lastmod = lastmod; | |
1382 | continue; | |
1383 | } else if (used) { | |
1384 | /* swapfile in use, not by this URL, log entry is newer */ | |
1385 | /* This is sorta bad: the log entry should NOT be newer at this | |
1386 | * point. If the log is dirty, the filesize check should have | |
1387 | * caught this. If the log is clean, there should never be a | |
1388 | * newer entry. */ | |
a3d5953d | 1389 | debug(20, 1) ("WARNING: newer swaplog entry for fileno %08X\n", |
30a4f2a8 | 1390 | sfileno); |
429fdbec | 1391 | /* I'm tempted to remove the swapfile here just to be safe, |
1392 | * but there is a bad race condition in the NOVM version if | |
1393 | * the swapfile has recently been opened for writing, but | |
1394 | * not yet opened for reading. Because we can't map | |
1395 | * swapfiles back to StoreEntrys, we don't know the state | |
1396 | * of the entry using that file. */ | |
ccbc48f5 | 1397 | /* We'll assume the existing entry is valid, probably because |
1398 | were in a slow rebuild and the the swap file number got taken | |
1399 | and the validation procedure hasn't run. */ | |
1400 | assert(RB->need_to_validate); | |
5608850b | 1401 | RB->clashcount++; |
30a4f2a8 | 1402 | continue; |
429fdbec | 1403 | } else if (e) { |
1404 | /* URL already exists, this swapfile not being used */ | |
1405 | /* junk old, load new */ | |
1406 | storeRelease(e); /* release old entry */ | |
1407 | RB->dupcount++; | |
1408 | } else { | |
1409 | /* URL doesnt exist, swapfile not in use */ | |
1410 | /* load new */ | |
1411 | (void) 0; | |
30a4f2a8 | 1412 | } |
1413 | /* update store_swap_size */ | |
c932b107 | 1414 | storeDirUpdateSwapSize(sfileno, size, 1); |
5608850b | 1415 | RB->objcount++; |
f5469bce | 1416 | e = storeAddDiskRestore(url, |
30a4f2a8 | 1417 | sfileno, |
1418 | (int) size, | |
1419 | expires, | |
f5469bce | 1420 | timestamp, |
311ea387 | 1421 | lastmod, |
1422 | d->clean); | |
5608850b | 1423 | storeDirSwapLog(e); |
a528eb87 | 1424 | HTTPCacheInfo->proto_newobject(HTTPCacheInfo, |
1ac9bc38 | 1425 | urlParseProtocol(url), |
30a4f2a8 | 1426 | (int) size, |
1427 | TRUE); | |
c943f331 | 1428 | } |
5608850b | 1429 | RB->rebuild_dir = d->next; |
76fefb77 | 1430 | for (D = &RB->rebuild_dir; *D; D = &(*D)->next); |
5608850b | 1431 | *D = d; |
1432 | d->next = NULL; | |
1433 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); | |
3facc31e | 1434 | } |
1435 | ||
0a0bf5db | 1436 | static void |
1437 | storeCleanup(void *data) | |
1438 | { | |
1439 | static storeCleanList *list = NULL; | |
1440 | storeCleanList *curr; | |
1441 | static int bucketnum = -1; | |
1442 | static int validnum = 0; | |
1443 | StoreEntry *e; | |
1444 | hash_link *link_ptr = NULL; | |
1445 | if (list == NULL) { | |
1446 | if (++bucketnum >= store_buckets) { | |
a3d5953d | 1447 | debug(20, 1) (" Completed Validation Procedure\n"); |
1448 | debug(20, 1) (" Validated %d Entries\n", validnum); | |
f182d1c5 | 1449 | store_rebuilding = 0; |
0a0bf5db | 1450 | return; |
1451 | } | |
1452 | link_ptr = hash_get_bucket(store_table, bucketnum); | |
1453 | for (; link_ptr; link_ptr = link_ptr->next) { | |
1454 | e = (StoreEntry *) link_ptr; | |
35ac11a3 | 1455 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1456 | continue; | |
1457 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) | |
1458 | continue; | |
311ea387 | 1459 | curr = xcalloc(1, sizeof(storeCleanList)); |
0a0bf5db | 1460 | curr->key = xstrdup(e->key); |
1461 | curr->next = list; | |
1462 | list = curr; | |
1463 | } | |
1464 | } | |
1465 | if (list == NULL) { | |
1466 | eventAdd("storeCleanup", storeCleanup, NULL, 0); | |
1467 | return; | |
1468 | } | |
1469 | curr = list; | |
1470 | list = list->next; | |
1471 | e = (StoreEntry *) hash_lookup(store_table, curr->key); | |
35ac11a3 | 1472 | if (e != NULL) { |
1473 | assert(!BIT_TEST(e->flag, ENTRY_VALIDATED)); | |
127d93d9 | 1474 | storeLockObject(e); |
3e347513 | 1475 | storeValidate(e, storeCleanupComplete, e); |
a3d5953d | 1476 | if ((++validnum & 0xFFF) == 0) |
1477 | debug(20, 1) (" %7d Entries Validated so far.\n", validnum); | |
35ac11a3 | 1478 | assert(validnum <= meta_data.store_entries); |
95c4b18f | 1479 | } |
0a0bf5db | 1480 | xfree(curr->key); |
1481 | xfree(curr); | |
1482 | eventAdd("storeCleanup", storeCleanup, NULL, 0); | |
1483 | } | |
1484 | ||
0a0bf5db | 1485 | static void |
35ac11a3 | 1486 | storeCleanupComplete(void *data) |
0a0bf5db | 1487 | { |
1488 | StoreEntry *e = data; | |
35ac11a3 | 1489 | storeUnlockObject(e); |
0a0bf5db | 1490 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1491 | storeRelease(e); | |
1492 | } | |
1493 | ||
1494 | static void | |
1495 | storeValidate(StoreEntry * e, VCB callback, void *callback_data) | |
1496 | { | |
1497 | valid_ctrl_t *ctrlp; | |
1498 | char *path; | |
1499 | struct stat *sb; | |
606bd3b5 | 1500 | #if !USE_ASYNC_IO |
95c4b18f | 1501 | int x; |
606bd3b5 | 1502 | #endif |
d7b78fbb | 1503 | assert(!BIT_TEST(e->flag, ENTRY_VALIDATED)); |
0a0bf5db | 1504 | if (e->swap_file_number < 0) { |
1505 | BIT_RESET(e->flag, ENTRY_VALIDATED); | |
35ac11a3 | 1506 | callback(callback_data); |
0a0bf5db | 1507 | return; |
1508 | } | |
1509 | path = storeSwapFullPath(e->swap_file_number, NULL); | |
1510 | sb = xmalloc(sizeof(struct stat)); | |
1511 | ctrlp = xmalloc(sizeof(valid_ctrl_t)); | |
1512 | ctrlp->sb = sb; | |
1513 | ctrlp->e = e; | |
1514 | ctrlp->callback = callback; | |
1515 | ctrlp->callback_data = callback_data; | |
1516 | #if USE_ASYNC_IO | |
1517 | aioStat(path, sb, storeValidateComplete, ctrlp); | |
1518 | #else | |
95c4b18f | 1519 | /* When evaluating the actual arguments in a function call, the order |
1520 | * in which the arguments and the function expression are evaluated is | |
1521 | * not specified; */ | |
1522 | x = stat(path, sb); | |
1523 | storeValidateComplete(ctrlp, x, errno); | |
0a0bf5db | 1524 | #endif |
1525 | return; | |
1526 | } | |
1527 | ||
1528 | static void | |
1529 | storeValidateComplete(void *data, int retcode, int errcode) | |
1530 | { | |
1531 | valid_ctrl_t *ctrlp = data; | |
1532 | struct stat *sb = ctrlp->sb; | |
1533 | StoreEntry *e = ctrlp->e; | |
1534 | char *path; | |
1535 | if (retcode < 0 && errcode == EWOULDBLOCK) { | |
1536 | path = storeSwapFullPath(e->swap_file_number, NULL); | |
1537 | retcode = stat(path, sb); | |
1538 | } | |
9e4ad609 | 1539 | if (retcode < 0 || sb->st_size == 0 || sb->st_size != e->object_len) { |
0a0bf5db | 1540 | BIT_RESET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 1541 | } else { |
0a0bf5db | 1542 | BIT_SET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 1543 | storeDirMapBitSet(e->swap_file_number); |
1544 | } | |
0a0bf5db | 1545 | errno = errcode; |
35ac11a3 | 1546 | ctrlp->callback(ctrlp->callback_data); |
0a0bf5db | 1547 | xfree(sb); |
1548 | xfree(ctrlp); | |
1549 | } | |
1550 | ||
3facc31e | 1551 | /* meta data recreated from disk image in swap directory */ |
b8d8561b | 1552 | static void |
5608850b | 1553 | storeRebuiltFromDisk(struct storeRebuildState *data) |
3facc31e | 1554 | { |
1555 | time_t r; | |
1556 | time_t stop; | |
97c03d3c | 1557 | stop = squid_curtime; |
3facc31e | 1558 | r = stop - data->start; |
a3d5953d | 1559 | debug(20, 1) ("Finished rebuilding storage from disk image.\n"); |
1560 | debug(20, 1) (" %7d Lines read from previous logfile.\n", data->linecount); | |
1561 | debug(20, 1) (" %7d Objects loaded.\n", data->objcount); | |
1562 | debug(20, 1) (" %7d Objects expired.\n", data->expcount); | |
1563 | debug(20, 1) (" %7d Duplicate URLs purged.\n", data->dupcount); | |
1564 | debug(20, 1) (" %7d Swapfile clashes avoided.\n", data->clashcount); | |
1565 | debug(20, 1) (" Took %d seconds (%6.1lf objects/sec).\n", | |
3facc31e | 1566 | r > 0 ? r : 0, (double) data->objcount / (r > 0 ? r : 1)); |
a3d5953d | 1567 | debug(20, 1) (" store_swap_size = %dk\n", store_swap_size); |
f182d1c5 | 1568 | if (data->need_to_validate) { |
a3d5953d | 1569 | debug(20, 1) ("Beginning Validation Procedure\n"); |
0a0bf5db | 1570 | eventAdd("storeCleanup", storeCleanup, NULL, 0); |
f182d1c5 | 1571 | } else { |
a3d5953d | 1572 | store_rebuilding = 0; |
0a0bf5db | 1573 | } |
f182d1c5 | 1574 | safe_free(data->line_in); |
1575 | safe_free(data); | |
090089c4 | 1576 | } |
1577 | ||
24382924 | 1578 | static void |
b8d8561b | 1579 | storeStartRebuildFromDisk(void) |
3facc31e | 1580 | { |
3facc31e | 1581 | int i; |
5608850b | 1582 | struct storeRebuildState *RB; |
1583 | struct _rebuild_dir *d; | |
1584 | FILE *fp; | |
1585 | int clean; | |
1586 | RB = xcalloc(1, sizeof(struct storeRebuildState)); | |
1587 | RB->start = squid_curtime; | |
1588 | for (i = 0; i < ncache_dirs; i++) { | |
1589 | fp = storeDirOpenTmpSwapLog(i, &clean); | |
1590 | if (fp == NULL) | |
1591 | continue; | |
1592 | d = xcalloc(1, sizeof(struct _rebuild_dir)); | |
1593 | d->dirn = i; | |
1594 | d->log = fp; | |
1595 | d->clean = clean; | |
1596 | d->speed = opt_foreground_rebuild ? 1 << 30 : 50; | |
1597 | d->next = RB->rebuild_dir; | |
1598 | RB->rebuild_dir = d; | |
1599 | if (!clean) | |
f182d1c5 | 1600 | RB->need_to_validate = 1; |
a3d5953d | 1601 | debug(20, 1) ("Rebuilding storage in Cache Dir #%d (%s)\n", |
5608850b | 1602 | i, clean ? "CLEAN" : "DIRTY"); |
1603 | } | |
1604 | RB->line_in_sz = 4096; | |
1605 | RB->line_in = xcalloc(1, RB->line_in_sz); | |
30a4f2a8 | 1606 | if (opt_foreground_rebuild) { |
5608850b | 1607 | storeDoRebuildFromDisk(RB); |
30a4f2a8 | 1608 | } else { |
5608850b | 1609 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); |
30a4f2a8 | 1610 | } |
3facc31e | 1611 | } |
090089c4 | 1612 | |
b8d8561b | 1613 | static int |
1614 | storeCheckSwapable(StoreEntry * e) | |
6602e70e | 1615 | { |
a7e59001 | 1616 | if (e->method != METHOD_GET) { |
a3d5953d | 1617 | debug(20, 2) ("storeCheckSwapable: NO: non-GET method\n"); |
1c481e00 | 1618 | } else if (!BIT_TEST(e->flag, ENTRY_CACHABLE)) { |
a3d5953d | 1619 | debug(20, 2) ("storeCheckSwapable: NO: not cachable\n"); |
6602e70e | 1620 | } else if (BIT_TEST(e->flag, RELEASE_REQUEST)) { |
a3d5953d | 1621 | debug(20, 2) ("storeCheckSwapable: NO: release requested\n"); |
6602e70e | 1622 | } else if (!storeEntryValidLength(e)) { |
a3d5953d | 1623 | debug(20, 2) ("storeCheckSwapable: NO: wrong content-length\n"); |
79b5cc5f | 1624 | } else if (BIT_TEST(e->flag, ENTRY_NEGCACHED)) { |
a3d5953d | 1625 | debug(20, 2) ("storeCheckSwapable: NO: negative cached\n"); |
3e98df20 | 1626 | return 0; /* avoid release call below */ |
38792624 | 1627 | } else if (e->mem_obj->e_current_len > Config.Store.maxObjectSize) { |
a3d5953d | 1628 | debug(20, 2) ("storeCheckSwapable: NO: too big\n"); |
d4432957 | 1629 | } else { |
6602e70e | 1630 | return 1; |
d4432957 | 1631 | } |
6602e70e | 1632 | |
2daae136 | 1633 | storeReleaseRequest(e); |
1c481e00 | 1634 | BIT_RESET(e->flag, ENTRY_CACHABLE); |
6602e70e | 1635 | return 0; |
1636 | } | |
1637 | ||
1638 | ||
090089c4 | 1639 | |
1640 | /* Complete transfer into the local cache. */ | |
b8d8561b | 1641 | void |
1642 | storeComplete(StoreEntry * e) | |
090089c4 | 1643 | { |
a3d5953d | 1644 | debug(20, 3) ("storeComplete: '%s'\n", e->key); |
22e4fa85 | 1645 | e->object_len = e->mem_obj->e_current_len; |
b8de7ebe | 1646 | e->lastref = squid_curtime; |
234967c9 | 1647 | e->store_status = STORE_OK; |
090089c4 | 1648 | storeSetMemStatus(e, IN_MEMORY); |
1649 | e->swap_status = NO_SWAP; | |
3a1c3e2f | 1650 | InvokeHandlers(e); |
aadcfbad | 1651 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
090089c4 | 1652 | storeRelease(e); |
12f6386d | 1653 | else if (storeCheckSwapable(e)) |
1654 | storeSwapOutStart(e); | |
1655 | else { | |
1656 | requestUnlink(e->mem_obj->request); | |
1657 | e->mem_obj->request = NULL; | |
1658 | } | |
090089c4 | 1659 | } |
1660 | ||
1661 | /* | |
1662 | * Fetch aborted. Tell all clients to go home. Negatively cache | |
1663 | * abort message, freeing the data for this object | |
1664 | */ | |
b8d8561b | 1665 | void |
0ee4272b | 1666 | storeAbort(StoreEntry * e, const char *msg) |
090089c4 | 1667 | { |
95d659f0 | 1668 | LOCAL_ARRAY(char, mime_hdr, 300); |
caebbe00 | 1669 | char *abort_msg; |
3e98df20 | 1670 | MemObject *mem = e->mem_obj; |
8f39b81d | 1671 | assert(e->store_status == STORE_PENDING); |
1672 | assert(mem != NULL); | |
a3d5953d | 1673 | /*assert(ping_status != PING_WAITING); */ |
1674 | debug(20, 6) ("storeAbort: '%s'\n", e->key); | |
79b5cc5f | 1675 | storeNegativeCache(e); |
234967c9 | 1676 | e->store_status = STORE_ABORTED; |
090089c4 | 1677 | storeSetMemStatus(e, IN_MEMORY); |
1678 | /* No DISK swap for negative cached object */ | |
1679 | e->swap_status = NO_SWAP; | |
b8de7ebe | 1680 | e->lastref = squid_curtime; |
95c4b18f | 1681 | storeLockObject(e); |
090089c4 | 1682 | /* Count bytes faulted through cache but not moved to disk */ |
a528eb87 | 1683 | HTTPCacheInfo->proto_touchobject(HTTPCacheInfo, |
3e98df20 | 1684 | mem->request ? mem->request->protocol : PROTO_NONE, |
1685 | mem->e_current_len); | |
090089c4 | 1686 | if (msg) { |
caebbe00 | 1687 | abort_msg = get_free_8k_page(); |
1688 | strcpy(abort_msg, "HTTP/1.0 400 Cache Detected Error\r\n"); | |
1689 | mk_mime_hdr(mime_hdr, | |
1690 | "text/html", | |
1691 | strlen(msg), | |
1692 | (time_t) Config.negativeTtl, | |
1693 | squid_curtime); | |
1694 | strcat(abort_msg, mime_hdr); | |
aadcfbad | 1695 | strcat(abort_msg, "\r\n"); |
caebbe00 | 1696 | strncat(abort_msg, msg, 8191 - strlen(abort_msg)); |
090089c4 | 1697 | storeAppend(e, abort_msg, strlen(abort_msg)); |
a1e47288 | 1698 | safe_free(mem->e_abort_msg); |
3e98df20 | 1699 | mem->e_abort_msg = xstrdup(abort_msg); |
090089c4 | 1700 | /* Set up object for negative caching */ |
1701 | BIT_SET(e->flag, ABORT_MSG_PENDING); | |
caebbe00 | 1702 | put_free_8k_page(abort_msg); |
090089c4 | 1703 | } |
1704 | /* We assign an object length here--The only other place we assign the | |
1705 | * object length is in storeComplete() */ | |
3e98df20 | 1706 | e->object_len = mem->e_current_len; |
bfcaf585 | 1707 | if (mem->abort.callback) { |
1708 | mem->abort.callback(mem->abort.data); | |
1709 | mem->abort.callback = NULL; | |
1710 | } | |
090089c4 | 1711 | InvokeHandlers(e); |
090089c4 | 1712 | storeUnlockObject(e); |
85b701ed | 1713 | return; |
090089c4 | 1714 | } |
1715 | ||
090089c4 | 1716 | /* get the first in memory object entry in the storage */ |
24382924 | 1717 | static StoreEntry * |
b8d8561b | 1718 | storeGetInMemFirst(void) |
090089c4 | 1719 | { |
1720 | hash_link *first = NULL; | |
c3c5b744 | 1721 | first = hash_first(in_mem_table); |
090089c4 | 1722 | return (first ? ((StoreEntry *) first->item) : NULL); |
1723 | } | |
1724 | ||
1725 | ||
1726 | /* get the next in memory object entry in the storage for a given | |
1727 | * search pointer */ | |
24382924 | 1728 | static StoreEntry * |
b8d8561b | 1729 | storeGetInMemNext(void) |
090089c4 | 1730 | { |
1731 | hash_link *next = NULL; | |
c3c5b744 | 1732 | next = hash_next(in_mem_table); |
090089c4 | 1733 | return (next ? ((StoreEntry *) next->item) : NULL); |
1734 | } | |
1735 | ||
1736 | /* get the first entry in the storage */ | |
b8d8561b | 1737 | StoreEntry * |
1738 | storeGetFirst(void) | |
090089c4 | 1739 | { |
c3c5b744 | 1740 | return ((StoreEntry *) hash_first(store_table)); |
090089c4 | 1741 | } |
1742 | ||
1743 | ||
1744 | /* get the next entry in the storage for a given search pointer */ | |
b8d8561b | 1745 | StoreEntry * |
1746 | storeGetNext(void) | |
090089c4 | 1747 | { |
c3c5b744 | 1748 | return ((StoreEntry *) hash_next(store_table)); |
090089c4 | 1749 | } |
1750 | ||
620da955 | 1751 | /* free up all ttl-expired objects */ |
48f44632 | 1752 | void |
1753 | storePurgeOld(void *unused) | |
090089c4 | 1754 | { |
1755 | StoreEntry *e = NULL; | |
090089c4 | 1756 | int n = 0; |
620da955 | 1757 | int count = 0; |
48f44632 | 1758 | /* reschedule */ |
1759 | eventAdd("storePurgeOld", storePurgeOld, NULL, Config.cleanRate); | |
090089c4 | 1760 | for (e = storeGetFirst(); e; e = storeGetNext()) { |
0a5b9b32 | 1761 | if ((++n & 0xFF) == 0) { |
2abe4822 | 1762 | if (shutdown_pending || reconfigure_pending) |
0a5b9b32 | 1763 | break; |
1764 | } | |
090089c4 | 1765 | if ((n & 0xFFF) == 0) |
a3d5953d | 1766 | debug(20, 2) ("storeWalkThrough: %7d objects so far.\n", n); |
429fdbec | 1767 | if (storeCheckExpired(e, 1)) |
cc61958c | 1768 | count += storeRelease(e); |
090089c4 | 1769 | } |
a3d5953d | 1770 | debug(20, 0) ("storePurgeOld: Removed %d objects\n", count); |
090089c4 | 1771 | } |
1772 | ||
1773 | ||
090089c4 | 1774 | /* Clear Memory storage to accommodate the given object len */ |
38792624 | 1775 | static void |
d4432957 | 1776 | storeGetMemSpace(int size) |
090089c4 | 1777 | { |
b32508fb | 1778 | StoreEntry *e = NULL; |
1779 | StoreEntry **list = NULL; | |
1780 | int list_count = 0; | |
090089c4 | 1781 | int n_expired = 0; |
66c2848f | 1782 | int n_purged = 0; |
579a45cd | 1783 | int n_released = 0; |
3bbba102 | 1784 | int n_locked = 0; |
090089c4 | 1785 | int i; |
b32508fb | 1786 | static time_t last_warning = 0; |
1787 | static time_t last_check = 0; | |
e954773d | 1788 | int pages_needed; |
090089c4 | 1789 | |
b32508fb | 1790 | if (squid_curtime == last_check) |
38792624 | 1791 | return; |
b32508fb | 1792 | last_check = squid_curtime; |
e954773d | 1793 | pages_needed = (size / SM_PAGE_SIZE) + 1; |
61cd203b | 1794 | if (sm_stats.n_pages_in_use + pages_needed < store_pages_high) |
38792624 | 1795 | return; |
311ea387 | 1796 | if (store_rebuilding) |
38792624 | 1797 | return; |
a3d5953d | 1798 | debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed); |
090089c4 | 1799 | |
579a45cd | 1800 | list = xcalloc(meta_data.mem_obj_count, sizeof(ipcache_entry *)); |
090089c4 | 1801 | for (e = storeGetInMemFirst(); e; e = storeGetInMemNext()) { |
579a45cd | 1802 | if (list_count == meta_data.mem_obj_count) |
b32508fb | 1803 | break; |
0a0bf5db | 1804 | if (storeEntryLocked(e)) |
1805 | continue; | |
429fdbec | 1806 | if (storeCheckExpired(e, 0)) { |
a3d5953d | 1807 | debug(20, 2) ("storeGetMemSpace: Expired: %s\n", e->url); |
090089c4 | 1808 | n_expired++; |
090089c4 | 1809 | storeRelease(e); |
579a45cd | 1810 | } else if (storeCheckPurgeMem(e)) { |
a3d5953d | 1811 | debug(20, 3) ("storeGetMemSpace: Adding '%s'\n", e->url); |
579a45cd | 1812 | *(list + list_count) = e; |
1813 | list_count++; | |
5e79098a | 1814 | } else if (!storeEntryLocked(e)) { |
a3d5953d | 1815 | debug(20, 3) ("storeGetMemSpace: Adding '%s'\n", e->url); |
579a45cd | 1816 | *(list + list_count) = e; |
1817 | list_count++; | |
1818 | } else { | |
1819 | n_locked++; | |
090089c4 | 1820 | } |
090089c4 | 1821 | } |
a3d5953d | 1822 | debug(20, 5) ("storeGetMemSpace: Sorting LRU_list: %7d items\n", list_count); |
b32508fb | 1823 | qsort((char *) list, |
1824 | list_count, | |
6e40f263 | 1825 | sizeof(StoreEntry *), |
582b6456 | 1826 | (QS *) compareSize); |
090089c4 | 1827 | |
1828 | /* Kick LRU out until we have enough memory space */ | |
b32508fb | 1829 | for (i = 0; i < list_count; i++) { |
e954773d | 1830 | if (sm_stats.n_pages_in_use + pages_needed < store_pages_low) |
9d90e665 | 1831 | break; |
579a45cd | 1832 | e = *(list + i); |
1833 | if (storeCheckPurgeMem(e)) { | |
5e79098a | 1834 | storePurgeMem(e); |
1835 | n_purged++; | |
579a45cd | 1836 | } else if (!storeEntryLocked(e)) { |
e954773d | 1837 | /* These will be neg-cached objects */ |
cc61958c | 1838 | n_released += storeRelease(e); |
579a45cd | 1839 | } else { |
85b701ed | 1840 | debug_trap("storeGetMemSpace: Bad Entry in LRU list"); |
579a45cd | 1841 | } |
090089c4 | 1842 | } |
1843 | ||
579a45cd | 1844 | i = 3; |
13c72789 | 1845 | if (sm_stats.n_pages_in_use > store_pages_max) { |
3bbba102 | 1846 | if (squid_curtime - last_warning > 600) { |
a3d5953d | 1847 | debug(20, 0) ("WARNING: Exceeded 'cache_mem' size (%dK > %dK)\n", |
13c72789 | 1848 | sm_stats.n_pages_in_use * 4, store_pages_max * 4); |
b32508fb | 1849 | last_warning = squid_curtime; |
a3d5953d | 1850 | debug(20, 0) ("Perhaps you should increase cache_mem?\n"); |
579a45cd | 1851 | i = 0; |
090089c4 | 1852 | } |
090089c4 | 1853 | } |
a3d5953d | 1854 | debug(20, i) ("storeGetMemSpace stats:\n"); |
1855 | debug(20, i) (" %6d objects locked in memory\n", n_locked); | |
1856 | debug(20, i) (" %6d LRU candidates\n", list_count); | |
1857 | debug(20, i) (" %6d were purged\n", n_purged); | |
1858 | debug(20, i) (" %6d were released\n", n_released); | |
3d700ed6 | 1859 | xfree(list); |
090089c4 | 1860 | } |
1861 | ||
b8d8561b | 1862 | static int |
1863 | compareSize(StoreEntry ** e1, StoreEntry ** e2) | |
090089c4 | 1864 | { |
6e40f263 | 1865 | if (!e1 || !e2) |
1866 | fatal_dump(NULL); | |
22e4fa85 | 1867 | if ((*e1)->mem_obj->e_current_len > (*e2)->mem_obj->e_current_len) |
090089c4 | 1868 | return (1); |
22e4fa85 | 1869 | if ((*e1)->mem_obj->e_current_len < (*e2)->mem_obj->e_current_len) |
090089c4 | 1870 | return (-1); |
090089c4 | 1871 | return (0); |
1872 | } | |
1873 | ||
b8d8561b | 1874 | static int |
1875 | compareLastRef(StoreEntry ** e1, StoreEntry ** e2) | |
090089c4 | 1876 | { |
1877 | if (!e1 || !e2) | |
1878 | fatal_dump(NULL); | |
090089c4 | 1879 | if ((*e1)->lastref > (*e2)->lastref) |
1880 | return (1); | |
090089c4 | 1881 | if ((*e1)->lastref < (*e2)->lastref) |
1882 | return (-1); | |
090089c4 | 1883 | return (0); |
1884 | } | |
1885 | ||
cb6ae8c3 | 1886 | static int |
d3c12084 | 1887 | compareBucketOrder(struct _bucketOrder *a, struct _bucketOrder *b) |
cb6ae8c3 | 1888 | { |
d3c12084 | 1889 | return a->index - b->index; |
cb6ae8c3 | 1890 | } |
1891 | ||
090089c4 | 1892 | /* returns the bucket number to work on, |
1893 | * pointer to next bucket after each calling | |
1894 | */ | |
24382924 | 1895 | static unsigned int |
b8d8561b | 1896 | storeGetBucketNum(void) |
090089c4 | 1897 | { |
1898 | static unsigned int bucket = 0; | |
66cedb85 | 1899 | if (bucket >= store_buckets) |
090089c4 | 1900 | bucket = 0; |
1901 | return (bucket++); | |
1902 | } | |
1903 | ||
66cedb85 | 1904 | #define SWAP_MAX_HELP (store_buckets/2) |
090089c4 | 1905 | |
1906 | /* The maximum objects to scan for maintain storage space */ | |
0a0bf5db | 1907 | #define SWAP_LRUSCAN_COUNT 1024 |
1908 | #define SWAP_LRU_REMOVE_COUNT 64 | |
090089c4 | 1909 | |
1910 | /* Clear Swap storage to accommodate the given object len */ | |
b8d8561b | 1911 | int |
1912 | storeGetSwapSpace(int size) | |
090089c4 | 1913 | { |
1914 | static int fReduceSwap = 0; | |
1915 | static int swap_help = 0; | |
a1b0d7cf | 1916 | StoreEntry *e = NULL; |
090089c4 | 1917 | int scanned = 0; |
1918 | int removed = 0; | |
090089c4 | 1919 | int locked = 0; |
1920 | int locked_size = 0; | |
a1b0d7cf | 1921 | int list_count = 0; |
1922 | int scan_count = 0; | |
1923 | int max_list_count = SWAP_LRUSCAN_COUNT << 1; | |
090089c4 | 1924 | int i; |
a1b0d7cf | 1925 | StoreEntry **LRU_list; |
090089c4 | 1926 | hash_link *link_ptr = NULL, *next = NULL; |
63017fae | 1927 | int kb_size = ((size + 1023) >> 10); |
090089c4 | 1928 | |
1929 | if (store_swap_size + kb_size <= store_swap_low) | |
1930 | fReduceSwap = 0; | |
1931 | if (!fReduceSwap && (store_swap_size + kb_size <= store_swap_high)) { | |
1932 | return 0; | |
1933 | } | |
a3d5953d | 1934 | debug(20, 2) ("storeGetSwapSpace: Starting...\n"); |
090089c4 | 1935 | |
1936 | /* Set flag if swap size over high-water-mark */ | |
1937 | if (store_swap_size + kb_size > store_swap_high) | |
1938 | fReduceSwap = 1; | |
1939 | ||
a3d5953d | 1940 | debug(20, 2) ("storeGetSwapSpace: Need %d bytes...\n", size); |
090089c4 | 1941 | |
a1b0d7cf | 1942 | LRU_list = xcalloc(max_list_count, sizeof(StoreEntry *)); |
1943 | /* remove expired objects until recover enough or no expired objects */ | |
66cedb85 | 1944 | for (i = 0; i < store_buckets; i++) { |
ec74cf5f | 1945 | link_ptr = hash_get_bucket(store_table, storeGetBucketNum()); |
090089c4 | 1946 | if (link_ptr == NULL) |
1947 | continue; | |
429fdbec | 1948 | /* this for loop handles one bucket of hash table */ |
79b5cc5f | 1949 | for (; link_ptr; link_ptr = next) { |
a1b0d7cf | 1950 | if (list_count == max_list_count) |
1951 | break; | |
090089c4 | 1952 | scanned++; |
1953 | next = link_ptr->next; | |
1954 | e = (StoreEntry *) link_ptr; | |
0a0bf5db | 1955 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1956 | continue; | |
429fdbec | 1957 | if (storeCheckExpired(e, 0)) { |
a3d5953d | 1958 | debug(20, 3) ("storeGetSwapSpace: Expired '%s'\n", e->url); |
429fdbec | 1959 | storeRelease(e); |
79b5cc5f | 1960 | } else if (!storeEntryLocked(e)) { |
fedac7e5 | 1961 | *(LRU_list + list_count) = e; |
a1b0d7cf | 1962 | list_count++; |
1963 | scan_count++; | |
090089c4 | 1964 | } else { |
ff5731b1 | 1965 | locked++; |
1966 | locked_size += e->mem_obj->e_current_len; | |
090089c4 | 1967 | } |
429fdbec | 1968 | } /* for, end of one bucket of hash table */ |
a1b0d7cf | 1969 | qsort((char *) LRU_list, |
1970 | list_count, | |
6e40f263 | 1971 | sizeof(StoreEntry *), |
582b6456 | 1972 | (QS *) compareLastRef); |
85b701ed | 1973 | if (list_count > SWAP_LRU_REMOVE_COUNT) |
fedac7e5 | 1974 | list_count = SWAP_LRU_REMOVE_COUNT; /* chop list */ |
a1b0d7cf | 1975 | if (scan_count > SWAP_LRUSCAN_COUNT) |
090089c4 | 1976 | break; |
1977 | } /* for */ | |
1978 | ||
30a4f2a8 | 1979 | #ifdef LOTSA_DEBUGGING |
090089c4 | 1980 | /* end of candidate selection */ |
a3d5953d | 1981 | debug(20, 2) ("storeGetSwapSpace: Current Size: %7d kbytes\n", |
30a4f2a8 | 1982 | store_swap_size); |
a3d5953d | 1983 | debug(20, 2) ("storeGetSwapSpace: High W Mark: %7d kbytes\n", |
30a4f2a8 | 1984 | store_swap_high); |
a3d5953d | 1985 | debug(20, 2) ("storeGetSwapSpace: Low W Mark: %7d kbytes\n", |
30a4f2a8 | 1986 | store_swap_low); |
a3d5953d | 1987 | debug(20, 2) ("storeGetSwapSpace: Entry count: %7d items\n", |
30a4f2a8 | 1988 | meta_data.store_entries); |
a3d5953d | 1989 | debug(20, 2) ("storeGetSwapSpace: Visited: %7d buckets\n", |
30a4f2a8 | 1990 | i + 1); |
a3d5953d | 1991 | debug(20, 2) ("storeGetSwapSpace: Scanned: %7d items\n", |
30a4f2a8 | 1992 | scanned); |
a3d5953d | 1993 | debug(20, 2) ("storeGetSwapSpace: Expired: %7d items\n", |
30a4f2a8 | 1994 | expired); |
a3d5953d | 1995 | debug(20, 2) ("storeGetSwapSpace: Locked: %7d items\n", |
30a4f2a8 | 1996 | locked); |
a3d5953d | 1997 | debug(20, 2) ("storeGetSwapSpace: Locked Space: %7d bytes\n", |
30a4f2a8 | 1998 | locked_size); |
a3d5953d | 1999 | debug(20, 2) ("storeGetSwapSpace: Scan in array: %7d bytes\n", |
30a4f2a8 | 2000 | scan_in_objs); |
a3d5953d | 2001 | debug(20, 2) ("storeGetSwapSpace: LRU candidate: %7d items\n", |
30a4f2a8 | 2002 | LRU_list->index); |
2003 | #endif /* LOTSA_DEBUGGING */ | |
090089c4 | 2004 | |
cc61958c | 2005 | for (i = 0; i < list_count; i++) |
2006 | removed += storeRelease(*(LRU_list + i)); | |
a1b0d7cf | 2007 | if (store_swap_size + kb_size <= store_swap_low) |
fedac7e5 | 2008 | fReduceSwap = 0; |
a3d5953d | 2009 | debug(20, 2) ("storeGetSwapSpace: After Freeing Size: %7d kbytes\n", |
a1b0d7cf | 2010 | store_swap_size); |
090089c4 | 2011 | /* free the list */ |
a1b0d7cf | 2012 | safe_free(LRU_list); |
090089c4 | 2013 | |
2014 | if ((store_swap_size + kb_size > store_swap_high)) { | |
63017fae | 2015 | i = 2; |
090089c4 | 2016 | if (++swap_help > SWAP_MAX_HELP) { |
a3d5953d | 2017 | debug(20, 0) ("WARNING: Repeated failures to free up disk space!\n"); |
63017fae | 2018 | i = 0; |
090089c4 | 2019 | } |
a3d5953d | 2020 | debug(20, i) ("storeGetSwapSpace: Disk usage is over high water mark\n"); |
2021 | debug(20, i) ("--> store_swap_high = %d KB\n", store_swap_high); | |
2022 | debug(20, i) ("--> store_swap_size = %d KB\n", store_swap_size); | |
2023 | debug(20, i) ("--> asking for %d KB\n", kb_size); | |
090089c4 | 2024 | } else { |
2025 | swap_help = 0; | |
2026 | } | |
a3d5953d | 2027 | debug(20, 2) ("Removed %d objects\n", removed); |
090089c4 | 2028 | return 0; |
2029 | } | |
2030 | ||
2031 | ||
2032 | /* release an object from a cache */ | |
cc61958c | 2033 | /* return number of objects released. */ |
b8d8561b | 2034 | int |
2035 | storeRelease(StoreEntry * e) | |
090089c4 | 2036 | { |
cc61958c | 2037 | StoreEntry *hentry = NULL; |
0ee4272b | 2038 | const char *hkey; |
a3d5953d | 2039 | debug(20, 3) ("storeRelease: Releasing: '%s'\n", e->key); |
090089c4 | 2040 | /* If, for any reason we can't discard this object because of an |
2041 | * outstanding request, mark it for pending release */ | |
2042 | if (storeEntryLocked(e)) { | |
9174e204 | 2043 | storeExpireNow(e); |
a3d5953d | 2044 | debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n"); |
2daae136 | 2045 | storeReleaseRequest(e); |
cc61958c | 2046 | return 0; |
090089c4 | 2047 | } |
cc61958c | 2048 | /* check if coresponding HEAD object exists. */ |
73ddce89 | 2049 | if (e->method == METHOD_GET) { |
cc61958c | 2050 | hkey = storeGeneratePublicKey(e->url, METHOD_HEAD); |
2051 | if ((hentry = (StoreEntry *) hash_lookup(store_table, hkey))) | |
38792624 | 2052 | storeExpireNow(hentry); |
090089c4 | 2053 | } |
311ea387 | 2054 | if (store_rebuilding) { |
a3d5953d | 2055 | debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n", |
30a4f2a8 | 2056 | e->key ? e->key : e->url ? e->url : "NO URL"); |
2057 | storeExpireNow(e); | |
2058 | storeSetPrivateKey(e); | |
35ac11a3 | 2059 | BIT_SET(e->flag, RELEASE_REQUEST); |
cc61958c | 2060 | return 0; |
30a4f2a8 | 2061 | } |
090089c4 | 2062 | if (e->swap_status == SWAP_OK && (e->swap_file_number > -1)) { |
9e4ad609 | 2063 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
2064 | storePutUnusedFileno(e->swap_file_number); | |
c932b107 | 2065 | storeDirUpdateSwapSize(e->swap_file_number, e->object_len, -1); |
090089c4 | 2066 | e->swap_file_number = -1; |
a528eb87 | 2067 | HTTPCacheInfo->proto_purgeobject(HTTPCacheInfo, |
1ac9bc38 | 2068 | urlParseProtocol(e->url), |
30a4f2a8 | 2069 | e->object_len); |
090089c4 | 2070 | } |
f7a5493b | 2071 | storeHashDelete(e); |
147d3115 | 2072 | storeLog(STORE_LOG_RELEASE, e); |
30a4f2a8 | 2073 | destroy_StoreEntry(e); |
cc61958c | 2074 | return 1; |
090089c4 | 2075 | } |
2076 | ||
090089c4 | 2077 | /* return 1 if a store entry is locked */ |
24382924 | 2078 | static int |
fe4e214f | 2079 | storeEntryLocked(const StoreEntry * e) |
090089c4 | 2080 | { |
30a4f2a8 | 2081 | if (e->lock_count) |
2082 | return 1; | |
2083 | if (e->swap_status == SWAPPING_OUT) | |
2084 | return 1; | |
2085 | if (e->mem_status == SWAPPING_IN) | |
2086 | return 1; | |
a1e47288 | 2087 | if (e->store_status == STORE_PENDING) |
2088 | return 1; | |
30a4f2a8 | 2089 | return 0; |
090089c4 | 2090 | } |
2091 | ||
b8d8561b | 2092 | static int |
fe4e214f | 2093 | storeCopy(const StoreEntry * e, int stateoffset, int maxSize, char *buf, int *size) |
090089c4 | 2094 | { |
429fdbec | 2095 | MemObject *mem = e->mem_obj; |
d89d1fb6 | 2096 | size_t s; |
2097 | assert(stateoffset >= mem->e_lowest_offset); | |
2098 | s = memCopy(mem->data, stateoffset, buf, maxSize); | |
429fdbec | 2099 | return *size = s; |
090089c4 | 2100 | } |
2101 | ||
2102 | /* check if there is any client waiting for this object at all */ | |
2103 | /* return 1 if there is at least one client */ | |
b8d8561b | 2104 | int |
fe4e214f | 2105 | storeClientWaiting(const StoreEntry * e) |
090089c4 | 2106 | { |
2107 | int i; | |
6e40f263 | 2108 | MemObject *mem = e->mem_obj; |
52d4522b | 2109 | if (mem->clients) { |
2110 | for (i = 0; i < mem->nclients; i++) { | |
382d851a | 2111 | if (mem->clients[i].callback_data != NULL) |
090089c4 | 2112 | return 1; |
2113 | } | |
2114 | } | |
2115 | return 0; | |
2116 | } | |
2117 | ||
b8d8561b | 2118 | static int |
382d851a | 2119 | storeClientListSearch(const MemObject * mem, void *data) |
090089c4 | 2120 | { |
2121 | int i; | |
52d4522b | 2122 | if (mem->clients) { |
2123 | for (i = 0; i < mem->nclients; i++) { | |
382d851a | 2124 | if (mem->clients[i].callback_data != data) |
6e40f263 | 2125 | continue; |
090089c4 | 2126 | return i; |
6e40f263 | 2127 | } |
090089c4 | 2128 | } |
2129 | return -1; | |
2130 | } | |
2131 | ||
2132 | /* add client with fd to client list */ | |
52d4522b | 2133 | int |
fe96bbe6 | 2134 | storeClientListAdd(StoreEntry * e, void *data) |
090089c4 | 2135 | { |
2136 | int i; | |
6e40f263 | 2137 | MemObject *mem = e->mem_obj; |
52d4522b | 2138 | struct _store_client *oldlist = NULL; |
6e40f263 | 2139 | int oldsize; |
f2ea1762 | 2140 | assert(mem != NULL); |
090089c4 | 2141 | /* look for empty slot */ |
52d4522b | 2142 | if (mem->clients == NULL) { |
2143 | mem->nclients = MIN_CLIENT; | |
d3a743b1 | 2144 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
090089c4 | 2145 | } |
52d4522b | 2146 | for (i = 0; i < mem->nclients; i++) { |
382d851a | 2147 | if (mem->clients[i].callback_data == data) |
429fdbec | 2148 | return i; /* its already here */ |
382d851a | 2149 | if (mem->clients[i].callback_data == NULL) |
6e40f263 | 2150 | break; |
2151 | } | |
52d4522b | 2152 | if (i == mem->nclients) { |
a3d5953d | 2153 | debug(20, 3) ("storeClientListAdd: Growing clients for '%s'\n", e->url); |
52d4522b | 2154 | oldlist = mem->clients; |
2155 | oldsize = mem->nclients; | |
2156 | mem->nclients <<= 1; | |
d3a743b1 | 2157 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
6e40f263 | 2158 | for (i = 0; i < oldsize; i++) |
52d4522b | 2159 | mem->clients[i] = oldlist[i]; |
6e40f263 | 2160 | safe_free(oldlist); |
52d4522b | 2161 | i = oldsize; |
6e40f263 | 2162 | } |
382d851a | 2163 | mem->clients[i].callback_data = data; |
fe96bbe6 | 2164 | mem->clients[i].seen_offset = 0; |
2165 | mem->clients[i].copy_offset = 0; | |
52d4522b | 2166 | return i; |
090089c4 | 2167 | } |
2168 | ||
2169 | /* same to storeCopy but also register client fd and last requested offset | |
2170 | * for each client */ | |
d89d1fb6 | 2171 | void |
38792624 | 2172 | storeClientCopy(StoreEntry * e, |
fe96bbe6 | 2173 | off_t seen_offset, |
2174 | off_t copy_offset, | |
d89d1fb6 | 2175 | size_t size, |
38792624 | 2176 | char *buf, |
d89d1fb6 | 2177 | STCB * callback, |
382d851a | 2178 | void *data) |
38792624 | 2179 | { |
2180 | int ci; | |
d89d1fb6 | 2181 | size_t sz; |
6e40f263 | 2182 | MemObject *mem = e->mem_obj; |
fe96bbe6 | 2183 | struct _store_client *sc; |
d4b1b930 | 2184 | static int recurse_detect = 0; |
fe96bbe6 | 2185 | assert(seen_offset <= mem->e_current_len); |
cb36fc2f | 2186 | assert(copy_offset >= mem->e_lowest_offset); |
f182d1c5 | 2187 | assert(recurse_detect < 3); /* could == 1 for IMS not modified's */ |
d89d1fb6 | 2188 | if ((ci = storeClientListSearch(mem, data)) < 0) |
2189 | fatal_dump("storeClientCopy: Unregistered client"); | |
fe96bbe6 | 2190 | sc = &mem->clients[ci]; |
2191 | sc->copy_offset = copy_offset; | |
2192 | sc->seen_offset = seen_offset; | |
2193 | if (seen_offset == mem->e_current_len) { | |
2194 | /* client has already seen this, wait for more */ | |
2195 | sc->callback = callback; | |
2196 | sc->copy_buf = buf; | |
2197 | sc->copy_size = size; | |
2198 | sc->copy_offset = copy_offset; | |
d89d1fb6 | 2199 | return; |
090089c4 | 2200 | } |
38f903c4 | 2201 | if (BIT_TEST(e->flag, DELETE_BEHIND)) |
2202 | storeDeleteBehind(e); | |
fe96bbe6 | 2203 | sz = memCopy(mem->data, copy_offset, buf, size); |
f182d1c5 | 2204 | recurse_detect++; |
d89d1fb6 | 2205 | callback(data, buf, sz); |
f182d1c5 | 2206 | recurse_detect--; |
090089c4 | 2207 | } |
2208 | ||
24382924 | 2209 | static int |
fe4e214f | 2210 | storeEntryValidLength(const StoreEntry * e) |
6602e70e | 2211 | { |
ffe4a367 | 2212 | int diff; |
2285407f | 2213 | int hdr_sz; |
2214 | int content_length; | |
ffe4a367 | 2215 | |
2285407f | 2216 | if (e->mem_obj == NULL) |
ffe4a367 | 2217 | fatal_dump("storeEntryValidLength: NULL mem_obj"); |
2218 | ||
2285407f | 2219 | hdr_sz = e->mem_obj->reply->hdr_sz; |
147d3115 | 2220 | content_length = e->mem_obj->reply->content_length; |
2285407f | 2221 | |
a3d5953d | 2222 | debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", e->key); |
2223 | debug(20, 5) ("storeEntryValidLength: object_len = %d\n", e->object_len); | |
2224 | debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n", hdr_sz); | |
2225 | debug(20, 5) ("storeEntryValidLength: content_length = %d\n", content_length); | |
ffe4a367 | 2226 | |
2285407f | 2227 | if (content_length == 0) { |
a3d5953d | 2228 | debug(20, 5) ("storeEntryValidLength: Zero content length; assume valid; '%s'\n", |
ffe4a367 | 2229 | e->key); |
2230 | return 1; | |
2231 | } | |
2285407f | 2232 | if (hdr_sz == 0) { |
a3d5953d | 2233 | debug(20, 5) ("storeEntryValidLength: Zero header size; assume valid; '%s'\n", |
ffe4a367 | 2234 | e->key); |
2235 | return 1; | |
2236 | } | |
2285407f | 2237 | diff = hdr_sz + content_length - e->object_len; |
ffe4a367 | 2238 | if (diff != 0) { |
a3d5953d | 2239 | debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n", |
ffe4a367 | 2240 | diff < 0 ? -diff : diff, |
2241 | diff < 0 ? "small" : "big", | |
2242 | e->key); | |
2243 | return 0; | |
2244 | } | |
2245 | return 1; | |
2246 | } | |
6602e70e | 2247 | |
d3c12084 | 2248 | #if HAVE_RANDOM |
2249 | #define squid_random random | |
2250 | #elif HAVE_LRAND48 | |
2251 | #define squid_random lrand48 | |
2252 | #else | |
2253 | #define squid_random rand | |
2254 | #endif | |
2255 | ||
63017fae | 2256 | static void |
2257 | storeRandomizeBuckets(void) | |
2258 | { | |
2259 | int i; | |
d3c12084 | 2260 | struct _bucketOrder *b; |
63017fae | 2261 | if (MaintBucketsOrder == NULL) |
d3c12084 | 2262 | MaintBucketsOrder = xcalloc(store_buckets, sizeof(struct _bucketOrder)); |
2263 | for (i = 0; i < store_buckets; i++) { | |
2264 | b = MaintBucketsOrder + i; | |
2265 | b->bucket = (unsigned int) i; | |
2266 | b->index = (int) squid_random(); | |
2267 | } | |
63017fae | 2268 | qsort((char *) MaintBucketsOrder, |
2269 | store_buckets, | |
d3c12084 | 2270 | sizeof(struct _bucketOrder), |
582b6456 | 2271 | (QS *) compareBucketOrder); |
63017fae | 2272 | } |
2273 | ||
66cedb85 | 2274 | static void |
2275 | storeInitHashValues(void) | |
2276 | { | |
2277 | int i; | |
a7e59001 | 2278 | /* Calculate size of hash table (maximum currently 64k buckets). */ |
38792624 | 2279 | i = Config.Swap.maxSize / Config.Store.avgObjectSize; |
a3d5953d | 2280 | debug(20, 1) ("Swap maxSize %d kB, estimated %d objects\n", |
66cedb85 | 2281 | Config.Swap.maxSize, i); |
38792624 | 2282 | i /= Config.Store.objectsPerBucket; |
a3d5953d | 2283 | debug(20, 1) ("Target number of buckets: %d\n", i); |
66cedb85 | 2284 | /* ideally the full scan period should be configurable, for the |
2285 | * moment it remains at approximately 24 hours. */ | |
2286 | if (i < 8192) | |
2287 | store_buckets = 7951, store_maintain_rate = 10; | |
2288 | else if (i < 12288) | |
2289 | store_buckets = 12149, store_maintain_rate = 7; | |
2290 | else if (i < 16384) | |
2291 | store_buckets = 16231, store_maintain_rate = 5; | |
2292 | else if (i < 32768) | |
2293 | store_buckets = 33493, store_maintain_rate = 2; | |
2294 | else | |
2295 | store_buckets = 65357, store_maintain_rate = 1; | |
2296 | store_maintain_buckets = 1; | |
63017fae | 2297 | storeRandomizeBuckets(); |
a3d5953d | 2298 | debug(20, 1) ("Using %d Store buckets, maintain %d bucket%s every %d second%s\n", |
66cedb85 | 2299 | store_buckets, |
2300 | store_maintain_buckets, | |
2301 | store_maintain_buckets == 1 ? null_string : "s", | |
2302 | store_maintain_rate, | |
2303 | store_maintain_rate == 1 ? null_string : "s"); | |
2304 | } | |
2305 | ||
b8d8561b | 2306 | void |
2307 | storeInit(void) | |
c943f331 | 2308 | { |
234967c9 | 2309 | char *fname = NULL; |
66cedb85 | 2310 | storeInitHashValues(); |
aadcfbad | 2311 | storeCreateHashTable(urlcmp); |
b6f794d6 | 2312 | if (strcmp((fname = Config.Log.store), "none") == 0) |
234967c9 | 2313 | storelog_fd = -1; |
2314 | else | |
f17936ab | 2315 | storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL); |
234967c9 | 2316 | if (storelog_fd < 0) |
a3d5953d | 2317 | debug(20, 1) ("Store logging disabled\n"); |
641941c0 | 2318 | if (ncache_dirs < 1) |
2319 | fatal("No cache_dir's specified in config file"); | |
e7a22b88 | 2320 | storeVerifySwapDirs(); |
2321 | storeDirOpenSwapLogs(); | |
b6f794d6 | 2322 | if (!opt_zap_disk_store) |
3facc31e | 2323 | storeStartRebuildFromDisk(); |
30a4f2a8 | 2324 | else |
311ea387 | 2325 | store_rebuilding = 0; |
b1c0cc67 | 2326 | } |
c943f331 | 2327 | |
b8d8561b | 2328 | void |
2329 | storeConfigure(void) | |
b1c0cc67 | 2330 | { |
e954773d | 2331 | int store_mem_high = 0; |
2332 | int store_mem_low = 0; | |
b6f794d6 | 2333 | store_mem_high = (long) (Config.Mem.maxSize / 100) * |
2334 | Config.Mem.highWaterMark; | |
2335 | store_mem_low = (long) (Config.Mem.maxSize / 100) * | |
2336 | Config.Mem.lowWaterMark; | |
090089c4 | 2337 | |
b1c0cc67 | 2338 | store_swap_high = (long) (((float) Config.Swap.maxSize * |
2339 | (float) Config.Swap.highWaterMark) / (float) 100); | |
2340 | store_swap_low = (long) (((float) Config.Swap.maxSize * | |
2341 | (float) Config.Swap.lowWaterMark) / (float) 100); | |
e954773d | 2342 | |
13c72789 | 2343 | store_pages_max = Config.Mem.maxSize / SM_PAGE_SIZE; |
e954773d | 2344 | store_pages_high = store_mem_high / SM_PAGE_SIZE; |
2345 | store_pages_low = store_mem_low / SM_PAGE_SIZE; | |
090089c4 | 2346 | } |
2347 | ||
b8d8561b | 2348 | int |
0ee4272b | 2349 | urlcmp(const char *url1, const char *url2) |
090089c4 | 2350 | { |
2351 | if (!url1 || !url2) | |
6eb42cae | 2352 | fatal_dump("urlcmp: Got a NULL url pointer."); |
090089c4 | 2353 | return (strcmp(url1, url2)); |
2354 | } | |
2355 | ||
090089c4 | 2356 | /* |
2357 | * This routine is to be called by main loop in main.c. | |
2358 | * It removes expired objects on only one bucket for each time called. | |
2359 | * returns the number of objects removed | |
2360 | * | |
2361 | * This should get called 1/s from main(). | |
2362 | */ | |
48f44632 | 2363 | void |
2364 | storeMaintainSwapSpace(void *unused) | |
090089c4 | 2365 | { |
30a4f2a8 | 2366 | static time_t last_time = 0; |
cb6ae8c3 | 2367 | static int bucket_index = 0; |
090089c4 | 2368 | hash_link *link_ptr = NULL, *next = NULL; |
2369 | StoreEntry *e = NULL; | |
2370 | int rm_obj = 0; | |
58104eab | 2371 | int scan_buckets = 0; |
66cedb85 | 2372 | int scan_obj = 0; |
d3c12084 | 2373 | static struct _bucketOrder *b; |
090089c4 | 2374 | |
48f44632 | 2375 | eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1); |
30a4f2a8 | 2376 | /* We can't delete objects while rebuilding swap */ |
311ea387 | 2377 | if (store_rebuilding) |
48f44632 | 2378 | return; |
30a4f2a8 | 2379 | |
090089c4 | 2380 | /* Purges expired objects, check one bucket on each calling */ |
66cedb85 | 2381 | if (squid_curtime - last_time >= store_maintain_rate) { |
58104eab | 2382 | for (;;) { |
2383 | if (scan_obj && scan_buckets >= store_maintain_buckets) | |
2384 | break; | |
2385 | if (++scan_buckets > 100) | |
2386 | break; | |
66cedb85 | 2387 | last_time = squid_curtime; |
cb6ae8c3 | 2388 | if (bucket_index >= store_buckets) { |
2389 | bucket_index = 0; | |
66cedb85 | 2390 | scan_revolutions++; |
a3d5953d | 2391 | debug(51, 1) ("Completed %d full expiration scans of store table\n", |
66cedb85 | 2392 | scan_revolutions); |
d25ad3e1 | 2393 | storeRandomizeBuckets(); |
66cedb85 | 2394 | } |
d3c12084 | 2395 | b = MaintBucketsOrder + bucket_index++; |
2396 | next = hash_get_bucket(store_table, b->bucket); | |
cc61958c | 2397 | while ((link_ptr = next)) { |
66cedb85 | 2398 | scan_obj++; |
2399 | next = link_ptr->next; | |
2400 | e = (StoreEntry *) link_ptr; | |
429fdbec | 2401 | if (!storeCheckExpired(e, 1)) |
66cedb85 | 2402 | continue; |
cc61958c | 2403 | rm_obj += storeRelease(e); |
66cedb85 | 2404 | } |
090089c4 | 2405 | } |
2406 | } | |
a3d5953d | 2407 | debug(51, rm_obj ? 2 : 9) ("Removed %d of %d objects from bucket %d\n", |
d3c12084 | 2408 | rm_obj, scan_obj, (int) b->bucket); |
79b5cc5f | 2409 | /* Scan row of hash table each second and free storage if we're |
2410 | * over the high-water mark */ | |
2411 | storeGetSwapSpace(0); | |
090089c4 | 2412 | } |
2413 | ||
090089c4 | 2414 | |
2415 | /* | |
5608850b | 2416 | * storeWriteCleanLogs |
090089c4 | 2417 | * |
2418 | * Writes a "clean" swap log file from in-memory metadata. | |
2419 | */ | |
b8d8561b | 2420 | int |
5608850b | 2421 | storeWriteCleanLogs(void) |
090089c4 | 2422 | { |
2423 | StoreEntry *e = NULL; | |
9e6c462c | 2424 | int *fd; |
5608850b | 2425 | char *line; |
090089c4 | 2426 | int n = 0; |
2427 | time_t start, stop, r; | |
10389b74 | 2428 | struct stat sb; |
9e6c462c | 2429 | char **cur; |
2430 | char **new; | |
2431 | char **cln; | |
5608850b | 2432 | int dirn; |
30a4f2a8 | 2433 | if (store_rebuilding) { |
a3d5953d | 2434 | debug(20, 1) ("Not currently OK to rewrite swap log.\n"); |
2435 | debug(20, 1) ("storeWriteCleanLogs: Operation aborted.\n"); | |
090089c4 | 2436 | return 0; |
2437 | } | |
a3d5953d | 2438 | debug(20, 1) ("storeWriteCleanLogs: Starting...\n"); |
97c03d3c | 2439 | start = squid_curtime; |
9e6c462c | 2440 | fd = xcalloc(ncache_dirs, sizeof(int)); |
2441 | cur = xcalloc(ncache_dirs, sizeof(char *)); | |
2442 | new = xcalloc(ncache_dirs, sizeof(char *)); | |
2443 | cln = xcalloc(ncache_dirs, sizeof(char *)); | |
3e347513 | 2444 | for (dirn = 0; dirn < ncache_dirs; dirn++) { |
2445 | fd[dirn] = -1; | |
2446 | cur[dirn] = xstrdup(storeDirSwapLogFile(dirn, NULL)); | |
2447 | new[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".clean")); | |
2448 | cln[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".last-clean")); | |
2449 | safeunlink(new[dirn], 1); | |
2450 | safeunlink(cln[dirn], 1); | |
f17936ab | 2451 | fd[dirn] = file_open(new[dirn], |
9e4ad609 | 2452 | O_WRONLY | O_CREAT | O_TRUNC, |
2453 | NULL, | |
2454 | NULL); | |
3e347513 | 2455 | if (fd[dirn] < 0) { |
a3d5953d | 2456 | debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror()); |
5608850b | 2457 | continue; |
2458 | } | |
4ac29a5b | 2459 | #if HAVE_FCHMOD |
3e347513 | 2460 | if (stat(cur[dirn], &sb) == 0) |
5608850b | 2461 | fchmod(fd[dirn], sb.st_mode); |
4ac29a5b | 2462 | #endif |
5608850b | 2463 | } |
2464 | line = xcalloc(1, 16384); | |
090089c4 | 2465 | for (e = storeGetFirst(); e; e = storeGetNext()) { |
090089c4 | 2466 | if (e->swap_file_number < 0) |
2467 | continue; | |
2468 | if (e->swap_status != SWAP_OK) | |
2469 | continue; | |
2470 | if (e->object_len <= 0) | |
2471 | continue; | |
3ba50d75 | 2472 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
0a21bd84 | 2473 | continue; |
2474 | if (BIT_TEST(e->flag, KEY_PRIVATE)) | |
2475 | continue; | |
5608850b | 2476 | if ((dirn = storeDirNumber(e->swap_file_number)) >= ncache_dirs) { |
2477 | debug_trap("storeWriteCleanLogss: dirn out of range"); | |
2478 | continue; | |
2479 | } | |
2480 | if (fd[dirn] < 0) | |
2481 | continue; | |
0a0bf5db | 2482 | sprintf(line, "%08x %08x %08x %08x %9d %s\n", |
f5469bce | 2483 | (int) e->swap_file_number, |
2484 | (int) e->timestamp, | |
2485 | (int) e->expires, | |
2486 | (int) e->lastmod, | |
2487 | e->object_len, | |
2488 | e->url); | |
5608850b | 2489 | if (write(fd[dirn], line, strlen(line)) < 0) { |
a3d5953d | 2490 | debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror()); |
2491 | debug(20, 0) ("storeWriteCleanLogs: Current swap logfile not replaced.\n"); | |
5608850b | 2492 | file_close(fd[dirn]); |
2493 | fd[dirn] = -1; | |
2494 | safeunlink(cln[dirn], 0); | |
2495 | continue; | |
30a4f2a8 | 2496 | } |
5608850b | 2497 | if ((++n & 0x3FFF) == 0) { |
a3d5953d | 2498 | debug(20, 1) (" %7d lines written so far.\n", n); |
090089c4 | 2499 | } |
2500 | } | |
5608850b | 2501 | safe_free(line); |
3e347513 | 2502 | for (dirn = 0; dirn < ncache_dirs; dirn++) { |
2503 | file_close(fd[dirn]); | |
2504 | fd[dirn] = -1; | |
2505 | if (rename(new[dirn], cur[dirn]) < 0) { | |
a3d5953d | 2506 | debug(50, 0) ("storeWriteCleanLogs: rename failed: %s\n", |
3e347513 | 2507 | xstrerror()); |
2508 | } | |
5608850b | 2509 | } |
2510 | storeDirCloseSwapLogs(); | |
2511 | storeDirOpenSwapLogs(); | |
97c03d3c | 2512 | stop = squid_curtime; |
090089c4 | 2513 | r = stop - start; |
a3d5953d | 2514 | debug(20, 1) (" Finished. Wrote %d lines.\n", n); |
2515 | debug(20, 1) (" Took %d seconds (%6.1lf lines/sec).\n", | |
090089c4 | 2516 | r > 0 ? r : 0, (double) n / (r > 0 ? r : 1)); |
0a0bf5db | 2517 | /* touch a timestamp file if we're not still validating */ |
3e347513 | 2518 | for (dirn = 0; dirn < ncache_dirs; dirn++) { |
f182d1c5 | 2519 | if (!store_rebuilding) |
f17936ab | 2520 | file_close(file_open(cln[dirn], |
3e347513 | 2521 | O_WRONLY | O_CREAT | O_TRUNC, NULL, NULL)); |
5608850b | 2522 | safe_free(cur[dirn]); |
2523 | safe_free(new[dirn]); | |
2524 | safe_free(cln[dirn]); | |
0a0bf5db | 2525 | } |
9e6c462c | 2526 | safe_free(cur); |
2527 | safe_free(new); | |
2528 | safe_free(cln); | |
2529 | safe_free(fd); | |
090089c4 | 2530 | return n; |
2531 | } | |
2532 | ||
24382924 | 2533 | static int |
b8d8561b | 2534 | swapInError(int fd_unused, StoreEntry * entry) |
090089c4 | 2535 | { |
b8de7ebe | 2536 | squid_error_entry(entry, ERR_DISK_IO, xstrerror()); |
090089c4 | 2537 | return 0; |
2538 | } | |
2539 | ||
b8d8561b | 2540 | int |
fe4e214f | 2541 | storePendingNClients(const StoreEntry * e) |
090089c4 | 2542 | { |
2543 | int npend = 0; | |
52d4522b | 2544 | MemObject *mem = e->mem_obj; |
090089c4 | 2545 | int i; |
52d4522b | 2546 | if (mem == NULL) |
090089c4 | 2547 | return 0; |
b012353a | 2548 | for (i = 0; i < mem->nclients; i++) { |
382d851a | 2549 | if (mem->clients[i].callback_data == NULL) |
52d4522b | 2550 | continue; |
b012353a | 2551 | npend++; |
090089c4 | 2552 | } |
2553 | return npend; | |
2554 | } | |
d8b45066 | 2555 | |
b8d8561b | 2556 | void |
2557 | storeRotateLog(void) | |
d8b45066 | 2558 | { |
2559 | char *fname = NULL; | |
2560 | int i; | |
95d659f0 | 2561 | LOCAL_ARRAY(char, from, MAXPATHLEN); |
2562 | LOCAL_ARRAY(char, to, MAXPATHLEN); | |
2edc2504 | 2563 | struct stat sb; |
d8b45066 | 2564 | |
234967c9 | 2565 | if (storelog_fd > -1) { |
2566 | file_close(storelog_fd); | |
2567 | storelog_fd = -1; | |
2568 | } | |
b6f794d6 | 2569 | if ((fname = Config.Log.store) == NULL) |
2d25dc1a | 2570 | return; |
234967c9 | 2571 | if (strcmp(fname, "none") == 0) |
2572 | return; | |
71d95578 | 2573 | #ifdef S_ISREG |
2edc2504 | 2574 | if (stat(fname, &sb) == 0) |
2575 | if (S_ISREG(sb.st_mode) == 0) | |
2576 | return; | |
71d95578 | 2577 | #endif |
234967c9 | 2578 | |
a3d5953d | 2579 | debug(20, 1) ("storeRotateLog: Rotating.\n"); |
d8b45066 | 2580 | |
2581 | /* Rotate numbers 0 through N up one */ | |
b6f794d6 | 2582 | for (i = Config.Log.rotateNumber; i > 1;) { |
2d25dc1a | 2583 | i--; |
2584 | sprintf(from, "%s.%d", fname, i - 1); | |
2585 | sprintf(to, "%s.%d", fname, i); | |
2586 | rename(from, to); | |
d8b45066 | 2587 | } |
2588 | /* Rotate the current log to .0 */ | |
b6f794d6 | 2589 | if (Config.Log.rotateNumber > 0) { |
2d25dc1a | 2590 | sprintf(to, "%s.%d", fname, 0); |
2591 | rename(fname, to); | |
d8b45066 | 2592 | } |
f17936ab | 2593 | storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL); |
234967c9 | 2594 | if (storelog_fd < 0) { |
a3d5953d | 2595 | debug(50, 0) ("storeRotateLog: %s: %s\n", fname, xstrerror()); |
2596 | debug(20, 1) ("Store logging disabled\n"); | |
234967c9 | 2597 | } |
d8b45066 | 2598 | } |
30a4f2a8 | 2599 | |
56f29785 | 2600 | static int |
fe4e214f | 2601 | storeShouldPurgeMem(const StoreEntry * e) |
56f29785 | 2602 | { |
2603 | if (storeCheckPurgeMem(e) == 0) | |
2604 | return 0; | |
e954773d | 2605 | if (sm_stats.n_pages_in_use > store_pages_low) |
56f29785 | 2606 | return 1; |
2607 | return 0; | |
2608 | } | |
2609 | ||
2610 | ||
30a4f2a8 | 2611 | /* |
2612 | * Check if its okay to remove the memory data for this object, but | |
2613 | * leave the StoreEntry around. Designed to be called from | |
2614 | * storeUnlockObject() and storeSwapOutHandle(). | |
2615 | */ | |
b8d8561b | 2616 | static int |
fe4e214f | 2617 | storeCheckPurgeMem(const StoreEntry * e) |
30a4f2a8 | 2618 | { |
2619 | if (storeEntryLocked(e)) | |
2620 | return 0; | |
2621 | if (e->store_status != STORE_OK) | |
2622 | return 0; | |
3e98df20 | 2623 | if (e->swap_status != SWAP_OK) |
2624 | return 0; | |
30a4f2a8 | 2625 | return 1; |
2626 | } | |
a1e47288 | 2627 | |
b8d8561b | 2628 | static int |
429fdbec | 2629 | storeCheckExpired(const StoreEntry * e, int check_lru_age) |
620da955 | 2630 | { |
429fdbec | 2631 | time_t max_age; |
620da955 | 2632 | if (storeEntryLocked(e)) |
2633 | return 0; | |
7cc1830f | 2634 | if (BIT_TEST(e->flag, ENTRY_NEGCACHED) && squid_curtime >= e->expires) |
2635 | return 1; | |
429fdbec | 2636 | if (!check_lru_age) |
2637 | return 0; | |
2638 | if ((max_age = storeExpiredReferenceAge()) <= 0) | |
db9ccd5a | 2639 | return 0; |
2640 | if (squid_curtime - e->lastref > max_age) | |
66cedb85 | 2641 | return 1; |
66cedb85 | 2642 | return 0; |
620da955 | 2643 | } |
2644 | ||
db9ccd5a | 2645 | /* |
2646 | * storeExpiredReferenceAge | |
2647 | * | |
429fdbec | 2648 | * The LRU age is scaled exponentially between 1 minute and |
2649 | * Config.referenceAge , when store_swap_low < store_swap_size < | |
2650 | * store_swap_high. This keeps store_swap_size within the low and high | |
2651 | * water marks. If the cache is very busy then store_swap_size stays | |
2652 | * closer to the low water mark, if it is not busy, then it will stay | |
2653 | * near the high water mark. The LRU age value can be examined on the | |
2654 | * cachemgr 'info' page. | |
fbdfb3cf | 2655 | */ |
35feb4aa | 2656 | time_t |
58104eab | 2657 | storeExpiredReferenceAge(void) |
2658 | { | |
58104eab | 2659 | double x; |
2660 | double z; | |
2661 | time_t age; | |
429fdbec | 2662 | if (Config.referenceAge == 0) |
2663 | return 0; | |
2664 | x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low); | |
2665 | x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; | |
2666 | z = pow((double) Config.referenceAge, x); | |
58104eab | 2667 | age = (time_t) (z * 60.0); |
2668 | if (age < 60) | |
2669 | age = 60; | |
2670 | else if (age > 31536000) | |
2671 | age = 31536000; | |
2672 | return age; | |
0a362c34 | 2673 | } |
2edc2504 | 2674 | |
b8d8561b | 2675 | void |
2676 | storeCloseLog(void) | |
457bedbd | 2677 | { |
15be2259 | 2678 | if (storelog_fd >= 0) |
40fcf5d4 | 2679 | file_close(storelog_fd); |
457bedbd | 2680 | } |
79b5cc5f | 2681 | |
b8d8561b | 2682 | void |
2683 | storeNegativeCache(StoreEntry * e) | |
79b5cc5f | 2684 | { |
ff5731b1 | 2685 | e->expires = squid_curtime + Config.negativeTtl; |
2686 | BIT_SET(e->flag, ENTRY_NEGCACHED); | |
79b5cc5f | 2687 | } |
0a21bd84 | 2688 | |
2689 | void | |
2690 | storeFreeMemory(void) | |
2691 | { | |
2692 | StoreEntry *e; | |
df92bd2a | 2693 | StoreEntry **list; |
2694 | int i = 0; | |
ea6b05e3 | 2695 | int j; |
56e15c50 | 2696 | list = xcalloc(meta_data.store_entries, sizeof(StoreEntry *)); |
df92bd2a | 2697 | e = (StoreEntry *) hash_first(store_table); |
2698 | while (e && i < meta_data.store_entries) { | |
2699 | *(list + i) = e; | |
2700 | i++; | |
2701 | e = (StoreEntry *) hash_next(store_table); | |
2702 | } | |
ea6b05e3 | 2703 | for (j = 0; j < i; j++) |
2704 | destroy_StoreEntry(*(list + j)); | |
df92bd2a | 2705 | xfree(list); |
0a21bd84 | 2706 | hashFreeMemory(store_table); |
e161be2e | 2707 | safe_free(MaintBucketsOrder); |
9e4ad609 | 2708 | storeDirCloseSwapLogs(); |
0a21bd84 | 2709 | } |
a7e59001 | 2710 | |
2711 | int | |
2712 | expiresMoreThan(time_t expires, time_t when) | |
2713 | { | |
48f44632 | 2714 | if (expires < 0) /* No Expires given */ |
2715 | return 1; | |
2716 | return (expires > (squid_curtime + when)); | |
a7e59001 | 2717 | } |
fe54d06d | 2718 | |
2719 | int | |
2720 | storeEntryValidToSend(StoreEntry * e) | |
2721 | { | |
2722 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) | |
2723 | return 0; | |
2724 | if (BIT_TEST(e->flag, ENTRY_NEGCACHED)) | |
44f78c24 | 2725 | if (e->expires <= squid_curtime) |
fe54d06d | 2726 | return 0; |
2727 | if (e->store_status == STORE_ABORTED) | |
2728 | return 0; | |
2729 | return 1; | |
2730 | } | |
62663274 | 2731 | |
ca98227c | 2732 | void |
2733 | storeTimestampsSet(StoreEntry * entry) | |
2734 | { | |
2735 | time_t served_date = -1; | |
2736 | struct _http_reply *reply = entry->mem_obj->reply; | |
2737 | served_date = reply->date > -1 ? reply->date : squid_curtime; | |
2738 | entry->expires = reply->expires; | |
2739 | if (reply->last_modified > -1) | |
2740 | entry->lastmod = reply->last_modified; | |
2741 | else | |
2742 | entry->lastmod = served_date; | |
2743 | entry->timestamp = served_date; | |
2744 | } | |
429fdbec | 2745 | |
2746 | #define FILENO_STACK_SIZE 128 | |
2747 | static int fileno_stack[FILENO_STACK_SIZE]; | |
2748 | int fileno_stack_count = 0; | |
2749 | ||
2750 | static int | |
2751 | storeGetUnusedFileno(void) | |
2752 | { | |
9e4ad609 | 2753 | int fn; |
429fdbec | 2754 | if (fileno_stack_count < 1) |
2755 | return -1; | |
9e4ad609 | 2756 | fn = fileno_stack[--fileno_stack_count]; |
2757 | storeDirMapBitSet(fn); | |
2758 | return fn; | |
429fdbec | 2759 | } |
2760 | ||
2761 | static void | |
2762 | storePutUnusedFileno(int fileno) | |
2763 | { | |
bf33177b | 2764 | assert(storeDirMapBitTest(fileno)); |
9e4ad609 | 2765 | storeDirMapBitReset(fileno); |
429fdbec | 2766 | if (fileno_stack_count < FILENO_STACK_SIZE) |
2767 | fileno_stack[fileno_stack_count++] = fileno; | |
2768 | else | |
2769 | unlinkdUnlink(storeSwapFullPath(fileno, NULL)); | |
2770 | } | |
bfcaf585 | 2771 | |
2772 | void | |
2773 | storeRegisterAbort(StoreEntry * e, STABH * cb, void *data) | |
2774 | { | |
2775 | MemObject *mem = e->mem_obj; | |
2776 | assert(mem); | |
2777 | assert(mem->abort.callback == NULL); | |
2778 | mem->abort.callback = cb; | |
2779 | mem->abort.data = data; | |
2780 | } | |
2781 | ||
2782 | void | |
2783 | storeUnregisterAbort(StoreEntry * e) | |
2784 | { | |
2785 | MemObject *mem = e->mem_obj; | |
2786 | assert(mem); | |
2787 | mem->abort.callback = NULL; | |
2788 | } |