]>
Commit | Line | Data |
---|---|---|
52e1d7e2 | 1 | |
66a91d2d | 2 | /* $Id: store.cc,v 1.53 1996/04/15 23:02:28 wessels Exp $ */ |
3 | #ident "$Id: store.cc,v 1.53 1996/04/15 23:02:28 wessels Exp $" | |
c943f331 | 4 | |
5 | /* | |
6 | * DEBUG: Section 20 store | |
7 | */ | |
090089c4 | 8 | |
9 | /* | |
10 | * Here is a summary of the routines which change mem_status and swap_status: | |
11 | * Added 11/18/95 | |
12 | * | |
13 | * Routine mem_status swap_status status | |
14 | * --------------------------------------------------------------------------- | |
6eb42cae | 15 | * storeCreateEntry NOT_IN_MEMORY NO_SWAP |
090089c4 | 16 | * storeComplete IN_MEMORY NO_SWAP |
17 | * storeSwapOutStart SWAPPING_OUT | |
18 | * storeSwapOutHandle(fail) NO_SWAP | |
19 | * storeSwapOutHandle(ok) SWAP_OK | |
20 | * --------------------------------------------------------------------------- | |
21 | * storeAddDiskRestore NOT_IN_MEMORY SWAP_OK | |
22 | * storeSwapInStart SWAPPING_IN | |
23 | * storeSwapInHandle(fail) NOT_IN_MEMORY | |
24 | * storeSwapInHandle(ok) IN_MEMORY | |
25 | * --------------------------------------------------------------------------- | |
26 | * storeAbort IN_MEMORY NO_SWAP | |
27 | * storePurgeMem NOT_IN_MEMORY | |
28 | * --------------------------------------------------------------------------- | |
29 | * You can reclaim an object's space if it's: | |
30 | * storeGetSwapSpace !SWAPPING_IN !SWAPPING_OUT !STORE_PENDING | |
31 | * | |
32 | */ | |
33 | ||
44a47c6e | 34 | #include "squid.h" /* goes first */ |
090089c4 | 35 | |
36 | #define REBUILD_TIMESTAMP_DELTA_MAX 2 | |
37 | #define MAX_SWAP_FILE (1<<21) | |
38 | #define SWAP_BUF DISK_PAGE_SIZE | |
39 | #define FATAL_BUF_SIZE 1024 | |
40 | #define SWAP_DIRECTORIES 100 | |
41 | #ifndef DEFAULT_SWAP_DIR | |
42 | #define DEFAULT_SWAP_DIR "/tmp/cache" | |
43 | #endif | |
44 | ||
227fbb74 | 45 | #define WITH_MEMOBJ 1 |
46 | #define WITHOUT_MEMOBJ 0 | |
47 | ||
090089c4 | 48 | /* rate of checking expired objects in main loop */ |
49 | #define STORE_MAINTAIN_RATE (20) | |
50 | ||
51 | #define STORE_BUCKETS (7921) | |
fc07a0e4 | 52 | #define STORE_IN_MEM_BUCKETS (143) |
090089c4 | 53 | |
2285407f | 54 | #define STORE_LOG_CREATE 0 |
55 | #define STORE_LOG_SWAPIN 1 | |
56 | #define STORE_LOG_SWAPOUT 2 | |
57 | #define STORE_LOG_RELEASE 3 | |
58 | ||
147d3115 | 59 | static char *storeLogTags[] = |
60 | { | |
61 | "CREATE", | |
62 | "SWAPIN", | |
63 | "SWAPOUT", | |
64 | "RELEASE" | |
2285407f | 65 | }; |
66 | ||
3facc31e | 67 | struct storeRebuild_data { |
68 | FILE *log; | |
69 | int objcount; /* # objects successfully reloaded */ | |
70 | int expcount; /* # objects expired */ | |
71 | int linecount; /* # lines parsed from cache logfile */ | |
72 | int clashcount; /* # swapfile clashes avoided */ | |
73 | int dupcount; /* # duplicates purged */ | |
74 | time_t start, stop; | |
75 | int fast_mode; | |
76 | char line_in[4096]; | |
77 | }; | |
78 | ||
090089c4 | 79 | /* Now, this table is inaccessible to outsider. They have to use a method |
80 | * to access a value in internal storage data structure. */ | |
81 | HashID table = 0; | |
82 | /* hash table for in-memory-only objects */ | |
83 | HashID in_mem_table = 0; | |
84 | ||
85 | /* initializtion flag */ | |
86 | static int ok_write_clean_log = 0; | |
87 | ||
88 | /* current memory storage size */ | |
89 | static unsigned long store_mem_size = 0; | |
90 | static unsigned long store_mem_high = 0; | |
91 | static unsigned long store_mem_low = 0; | |
92 | ||
93 | /* current hotvm object */ | |
94 | /* defaults for 16M cache and 12.5 cache_hot_vm_factor */ | |
95 | static int store_hotobj_high = 180; | |
96 | static int store_hotobj_low = 120; | |
97 | ||
98 | ||
99 | /* current file name, swap file, use number as a filename */ | |
100 | static unsigned long swapfileno = 0; | |
101 | static int store_swap_size = 0; /* kilobytes !! */ | |
102 | static unsigned long store_swap_high = 0; | |
103 | static unsigned long store_swap_low = 0; | |
2285407f | 104 | static int swaplog_fd = -1; |
105 | static int swaplog_lock = 0; | |
2285407f | 106 | static int storelog_fd = -1; |
090089c4 | 107 | |
090089c4 | 108 | /* key temp buffer */ |
109 | static char key_temp_buffer[MAX_URL]; | |
c943f331 | 110 | static char swaplog_file[MAX_FILE_NAME_LEN]; |
3facc31e | 111 | static char tmp_filename[MAX_FILE_NAME_LEN]; |
66a91d2d | 112 | static char logmsg[MAX_URL << 1]; |
090089c4 | 113 | |
114 | /* patch cache_dir to accomodate multiple disk storage */ | |
115 | dynamic_array *cache_dirs = NULL; | |
116 | int ncache_dirs = 0; | |
117 | ||
227fbb74 | 118 | static MemObject *new_MemObject() |
119 | { | |
120 | MemObject *m = NULL; | |
2285407f | 121 | m = (MemObject *) xcalloc(1, sizeof(MemObject)); |
147d3115 | 122 | m->reply = (struct _http_reply *) xcalloc(1, sizeof(struct _http_reply)); |
227fbb74 | 123 | meta_data.store_in_mem_objects++; |
124 | debug(20, 3, "new_MemObject: returning %p\n", m); | |
125 | return m; | |
126 | } | |
127 | ||
128 | static StoreEntry *new_StoreEntry(mem_obj_flag) | |
fc07a0e4 | 129 | int mem_obj_flag; |
090089c4 | 130 | { |
131 | StoreEntry *e = NULL; | |
132 | ||
133 | e = (StoreEntry *) xcalloc(1, sizeof(StoreEntry)); | |
227fbb74 | 134 | meta_data.store_entries++; |
135 | if (mem_obj_flag) | |
136 | e->mem_obj = new_MemObject(); | |
137 | debug(20, 3, "new_StoreEntry: returning %p\n", e); | |
138 | return e; | |
090089c4 | 139 | } |
140 | ||
227fbb74 | 141 | static void destroy_MemObject(m) |
142 | MemObject *m; | |
090089c4 | 143 | { |
227fbb74 | 144 | debug(20, 3, "destroy_MemObject: destroying %p\n", m); |
2285407f | 145 | safe_free(m->mime_hdr); |
146 | safe_free(m->reply); | |
227fbb74 | 147 | xfree(m); |
227fbb74 | 148 | meta_data.store_in_mem_objects--; |
090089c4 | 149 | } |
150 | ||
227fbb74 | 151 | static void destroy_StoreEntry(e) |
090089c4 | 152 | StoreEntry *e; |
153 | { | |
227fbb74 | 154 | debug(20, 3, "destroy_StoreEntry: destroying %p\n", e); |
155 | if (!e) | |
6eb42cae | 156 | fatal_dump("destroy_StoreEntry: NULL Entry"); |
227fbb74 | 157 | if (e->mem_obj) |
158 | destroy_MemObject(e->mem_obj); | |
159 | xfree(e); | |
160 | meta_data.store_entries--; | |
090089c4 | 161 | } |
162 | ||
227fbb74 | 163 | static mem_ptr new_MemObjectData() |
164 | { | |
165 | debug(20, 3, "new_MemObjectData: calling memInit()\n"); | |
166 | meta_data.hot_vm++; | |
167 | return memInit(); | |
168 | } | |
090089c4 | 169 | |
227fbb74 | 170 | static void destroy_MemObjectData(m) |
171 | MemObject *m; | |
090089c4 | 172 | { |
227fbb74 | 173 | debug(20, 3, "destroy_MemObjectData: destroying %p\n", m->data); |
174 | store_mem_size -= m->e_current_len - m->e_lowest_offset; | |
175 | debug(20, 8, "destroy_MemObjectData: Freeing %d in-memory bytes\n", | |
176 | m->e_current_len); | |
177 | debug(20, 8, "destroy_MemObjectData: store_mem_size = %d\n", | |
178 | store_mem_size); | |
179 | if (m->data) { | |
180 | m->data->mem_free(m->data); | |
181 | m->data = NULL; | |
182 | meta_data.hot_vm--; | |
090089c4 | 183 | } |
227fbb74 | 184 | m->e_current_len = 0; |
090089c4 | 185 | } |
186 | ||
090089c4 | 187 | /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */ |
188 | ||
189 | /* | |
190 | * Create 2 hash tables, "table" has all objects, "in_mem_table" has only | |
191 | * objects in the memory. | |
192 | */ | |
193 | ||
194 | HashID storeCreateHashTable(cmp_func) | |
195 | int (*cmp_func) (char *, char *); | |
196 | { | |
197 | table = hash_create(cmp_func, STORE_BUCKETS); | |
198 | in_mem_table = hash_create(cmp_func, STORE_IN_MEM_BUCKETS); | |
199 | return (table); | |
200 | } | |
201 | ||
202 | /* | |
203 | * if object is in memory, also insert into in_mem_table | |
204 | */ | |
205 | ||
6602e70e | 206 | static int storeHashInsert(e) |
090089c4 | 207 | StoreEntry *e; |
208 | { | |
227fbb74 | 209 | debug(20, 3, "storeHashInsert: Inserting Entry %p key '%s'\n", |
210 | e, e->key); | |
090089c4 | 211 | if (e->mem_status == IN_MEMORY) |
212 | hash_insert(in_mem_table, e->key, e); | |
213 | return (hash_join(table, (hash_link *) e)); | |
214 | } | |
215 | ||
216 | /* | |
217 | * if object in memory, also remove from in_mem_table | |
218 | */ | |
219 | ||
220 | int storeHashDelete(hash_ptr) | |
221 | hash_link *hash_ptr; | |
222 | { | |
223 | hash_link *hptr = NULL; | |
224 | StoreEntry *e = NULL; | |
225 | ||
226 | e = (StoreEntry *) hash_ptr; | |
227 | if (e->mem_status == IN_MEMORY && e->key) { | |
228 | if ((hptr = hash_lookup(in_mem_table, e->key))) | |
229 | hash_delete_link(in_mem_table, hptr); | |
230 | } | |
231 | return (hash_remove_link(table, hash_ptr)); | |
232 | } | |
233 | ||
234 | /* | |
235 | * maintain the in-mem hash table according to the changes of mem_status | |
236 | * This routine replaces the instruction "e->status = status;" | |
237 | */ | |
238 | ||
239 | void storeSetMemStatus(e, status) | |
240 | StoreEntry *e; | |
241 | int status; | |
242 | { | |
243 | hash_link *ptr = NULL; | |
244 | ||
6eb42cae | 245 | if (e->key == NULL) |
246 | fatal_dump("storeSetMemStatus: NULL key"); | |
247 | ||
248 | if (status != IN_MEMORY && e->mem_status == IN_MEMORY) { | |
249 | if ((ptr = hash_lookup(in_mem_table, e->key))) | |
250 | hash_delete_link(in_mem_table, ptr); | |
251 | } else if (status == IN_MEMORY && e->mem_status != IN_MEMORY) { | |
252 | hash_insert(in_mem_table, e->key, e); | |
090089c4 | 253 | } |
254 | e->mem_status = status; | |
255 | } | |
256 | ||
257 | /* -------------------------------------------------------------------------- */ | |
258 | ||
259 | /* free whole entry */ | |
260 | void storeFreeEntry(e) | |
261 | StoreEntry *e; | |
262 | { | |
227fbb74 | 263 | int i; |
090089c4 | 264 | |
227fbb74 | 265 | if (!e) |
6eb42cae | 266 | fatal_dump("storeFreeEntry: NULL Entry"); |
090089c4 | 267 | |
2285407f | 268 | debug(20, 3, "storeFreeEntry: Freeing %s\n", e->key); |
090089c4 | 269 | |
2aca8433 | 270 | if (e->mem_obj) { |
227fbb74 | 271 | destroy_MemObjectData(e->mem_obj); |
272 | e->mem_obj->data = NULL; | |
090089c4 | 273 | } |
274 | meta_data.url_strings -= strlen(e->url); | |
275 | safe_free(e->url); | |
2aca8433 | 276 | if (BIT_TEST(e->flag, KEY_URL)) |
277 | e->key = NULL; | |
278 | else | |
090089c4 | 279 | safe_free(e->key); |
2aca8433 | 280 | if (e->mem_obj) { |
22e4fa85 | 281 | safe_free(e->mem_obj->mime_hdr); |
090089c4 | 282 | /* Leave an unzeroed pointer to the abort msg for posterity */ |
f9a988f1 | 283 | safe_free(e->mem_obj->e_abort_msg); |
22e4fa85 | 284 | safe_free(e->mem_obj->pending); |
090089c4 | 285 | /* look up to free client_list */ |
22e4fa85 | 286 | if (e->mem_obj->client_list) { |
f9a988f1 | 287 | for (i = 0; i < e->mem_obj->client_list_size; ++i) |
3facc31e | 288 | safe_free(e->mem_obj->client_list[i]); |
22e4fa85 | 289 | safe_free(e->mem_obj->client_list); |
090089c4 | 290 | } |
291 | } | |
292 | destroy_StoreEntry(e); | |
293 | } | |
294 | ||
147d3115 | 295 | static char *time_describe(t) |
296 | time_t t; | |
2285407f | 297 | { |
147d3115 | 298 | static char buf[128]; |
2285407f | 299 | |
147d3115 | 300 | if (t < 60) { |
301 | sprintf(buf, "%ds", (int) t); | |
302 | } else if (t < 3600) { | |
303 | sprintf(buf, "%dm", (int) t / 60); | |
304 | } else if (t < 86400) { | |
305 | sprintf(buf, "%dh", (int) t / 3600); | |
306 | } else if (t < 604800) { | |
307 | sprintf(buf, "%dD", (int) t / 86400); | |
308 | } else if (t < 2592000) { | |
309 | sprintf(buf, "%dW", (int) t / 604800); | |
310 | } else if (t < 31536000) { | |
311 | sprintf(buf, "%dM", (int) t / 2592000); | |
312 | } else { | |
313 | sprintf(buf, "%dY", (int) t / 31536000); | |
314 | } | |
315 | return buf; | |
2285407f | 316 | } |
317 | ||
318 | static void storeLog(tag, e) | |
319 | int tag; | |
320 | StoreEntry *e; | |
321 | { | |
322 | time_t t; | |
323 | int expect_len = 0; | |
324 | int actual_len = 0; | |
325 | int code = 0; | |
2285407f | 326 | if (storelog_fd < 0) |
327 | return; | |
328 | t = e->expires - cached_curtime; | |
329 | if (e->mem_obj) { | |
330 | code = e->mem_obj->reply->code; | |
331 | expect_len = (int) e->mem_obj->reply->content_length; | |
332 | actual_len = (int) e->mem_obj->e_current_len - e->mem_obj->reply->hdr_sz; | |
333 | } | |
334 | sprintf(logmsg, "%9d.%03d %-7s %4d %9d [%3s] %d/%d %s\n", | |
335 | (int) current_time.tv_sec, | |
336 | (int) current_time.tv_usec / 1000, | |
337 | storeLogTags[tag], | |
338 | code, | |
339 | (int) t, | |
340 | time_describe(t), | |
341 | expect_len, | |
342 | actual_len, | |
343 | e->key); | |
344 | file_write(storelog_fd, | |
345 | xstrdup(logmsg), | |
346 | strlen(logmsg), | |
347 | 0, | |
348 | NULL, | |
349 | NULL); | |
350 | } | |
351 | ||
090089c4 | 352 | |
353 | /* get rid of memory copy of the object */ | |
354 | void storePurgeMem(e) | |
355 | StoreEntry *e; | |
356 | { | |
2285407f | 357 | debug(20, 3, "storePurgeMem: Freeing memory-copy of %s\n", e->key); |
2aca8433 | 358 | if (e->mem_obj == NULL) |
090089c4 | 359 | return; |
360 | ||
361 | if (storeEntryLocked(e)) { | |
c943f331 | 362 | debug(20, 0, "storePurgeMem: someone (storeGetMemSpace?) is purging a locked object?\n"); |
363 | debug(20, 0, "%s", storeToString(e)); | |
090089c4 | 364 | fatal_dump(NULL); |
365 | } | |
227fbb74 | 366 | destroy_MemObjectData(e->mem_obj); |
367 | e->mem_obj->data = NULL; | |
c943f331 | 368 | debug(20, 8, "storePurgeMem: Freeing %d in-memory bytes\n", |
090089c4 | 369 | e->object_len); |
c943f331 | 370 | debug(20, 8, "storePurgeMem: store_mem_size = %d\n", store_mem_size); |
090089c4 | 371 | storeSetMemStatus(e, NOT_IN_MEMORY); |
22e4fa85 | 372 | e->mem_obj->e_current_len = 0; |
090089c4 | 373 | /* free up pending list table */ |
22e4fa85 | 374 | safe_free(e->mem_obj->pending); |
375 | e->mem_obj->pending_list_size = 0; | |
090089c4 | 376 | /* free up client list table and entries */ |
22e4fa85 | 377 | if (e->mem_obj->client_list) { |
090089c4 | 378 | int i; |
22e4fa85 | 379 | for (i = 0; i < e->mem_obj->client_list_size; ++i) { |
380 | if (e->mem_obj->client_list[i]) | |
381 | safe_free(e->mem_obj->client_list[i]); | |
090089c4 | 382 | } |
22e4fa85 | 383 | safe_free(e->mem_obj->client_list); |
090089c4 | 384 | } |
227fbb74 | 385 | destroy_MemObject(e->mem_obj); |
386 | e->mem_obj = NULL; | |
090089c4 | 387 | } |
388 | ||
389 | /* lock the object for reading, start swapping in if necessary */ | |
390 | int storeLockObject(e) | |
391 | StoreEntry *e; | |
392 | { | |
393 | int swap_in_stat = 0; | |
394 | int status = 0; | |
395 | ||
396 | e->lock_count++; | |
2285407f | 397 | debug(20, 3, "storeLockObject: locks %d: '%s'\n", e->lock_count, e->key); |
090089c4 | 398 | |
399 | if ((e->mem_status == NOT_IN_MEMORY) && /* Not in memory */ | |
400 | (e->swap_status != SWAP_OK) && /* Not on disk */ | |
401 | (e->status != STORE_PENDING) /* Not being fetched */ | |
402 | ) { | |
c943f331 | 403 | debug(20, 0, "storeLockObject: NOT_IN_MEMORY && !SWAP_OK && !STORE_PENDING conflict: <URL:%s>. aborting...\n", e->url); |
090089c4 | 404 | /* If this sanity check fails, we should just ... */ |
405 | fatal_dump(NULL); | |
406 | } | |
407 | e->lastref = cached_curtime; | |
408 | ||
409 | /* StoreLockObject() is called during icp_hit_or_miss and once by storeAbort | |
410 | * If the object is NOT_IN_MEMORY, fault it in. */ | |
411 | if ((e->mem_status == NOT_IN_MEMORY) && (e->swap_status == SWAP_OK)) { | |
412 | /* object is in disk and no swapping daemon running. Bring it in. */ | |
413 | if ((swap_in_stat = storeSwapInStart(e)) < 0) { | |
414 | /* | |
415 | * We couldn't find or couldn't open object's swapfile. | |
416 | * So, return a -1 here, indicating that we will treat | |
417 | * the reference like a MISS_TTL, force a keychange and | |
418 | storeRelease. */ | |
419 | e->lock_count--; | |
420 | } | |
421 | status = swap_in_stat; | |
422 | } | |
423 | return status; | |
424 | } | |
425 | ||
2daae136 | 426 | void storeReleaseRequest(e) |
147d3115 | 427 | StoreEntry *e; |
2285407f | 428 | { |
58bcd31b | 429 | if (e->flag & RELEASE_REQUEST) |
430 | return; | |
2daae136 | 431 | debug(20, 3, "storeReleaseRequest: FOR '%s'\n", e->key ? e->key : e->url); |
d3c1a245 | 432 | e->flag |= RELEASE_REQUEST; |
2285407f | 433 | } |
434 | ||
090089c4 | 435 | /* unlock object, return -1 if object get released after unlock |
436 | * otherwise lock_count */ | |
437 | ||
438 | int storeUnlockObject(e) | |
439 | StoreEntry *e; | |
440 | { | |
441 | int e_lock_count; | |
442 | ||
ffe4a367 | 443 | debug(20, 3, "storeUnlockObject: key '%s' count=%d\n", e->key, e->lock_count); |
6602e70e | 444 | |
090089c4 | 445 | if ((int) e->lock_count > 0) |
446 | e->lock_count--; | |
447 | else if (e->lock_count == 0) { | |
c943f331 | 448 | debug(20, 0, "Entry lock count %d is out-of-whack\n", e->lock_count); |
090089c4 | 449 | } |
450 | /* Prevent UMR if we end up freeing the entry */ | |
451 | e_lock_count = (int) e->lock_count; | |
452 | ||
453 | if (e->lock_count == 0) { | |
090089c4 | 454 | if (e->flag & RELEASE_REQUEST) { |
455 | storeRelease(e); | |
456 | } else if (e->flag & ABORT_MSG_PENDING) { | |
457 | /* This is where the negative cache gets storeAppended */ | |
458 | /* Briefly lock to replace content with abort message */ | |
459 | e->lock_count++; | |
227fbb74 | 460 | destroy_MemObjectData(e->mem_obj); |
461 | e->object_len = 0; | |
462 | e->mem_obj->data = new_MemObjectData(); | |
22e4fa85 | 463 | storeAppend(e, e->mem_obj->e_abort_msg, strlen(e->mem_obj->e_abort_msg)); |
464 | e->object_len = e->mem_obj->e_current_len | |
465 | = strlen(e->mem_obj->e_abort_msg); | |
090089c4 | 466 | BIT_RESET(e->flag, ABORT_MSG_PENDING); |
467 | e->lock_count--; | |
468 | } | |
469 | } | |
470 | return e_lock_count; | |
471 | ||
472 | } | |
473 | ||
474 | /* Lookup an object in the cache. | |
475 | * return just a reference to object, don't start swapping in yet. */ | |
476 | StoreEntry *storeGet(url) | |
477 | char *url; | |
478 | { | |
479 | hash_link *hptr = NULL; | |
480 | ||
6602e70e | 481 | debug(20, 3, "storeGet: looking up %s\n", url); |
090089c4 | 482 | |
227fbb74 | 483 | if ((hptr = hash_lookup(table, url)) != NULL) |
484 | return (StoreEntry *) hptr; | |
090089c4 | 485 | return NULL; |
486 | } | |
487 | ||
04e8dbaa | 488 | unsigned int getKeyCounter() |
489 | { | |
2d25dc1a | 490 | static unsigned int key_counter = 0; |
491 | if (++key_counter == 0) | |
492 | ++key_counter; | |
493 | return key_counter; | |
04e8dbaa | 494 | } |
495 | ||
73ddce89 | 496 | char *storeGeneratePrivateKey(url, method, num) |
6eb42cae | 497 | char *url; |
73ddce89 | 498 | int method; |
6eb42cae | 499 | int num; |
500 | { | |
6eb42cae | 501 | if (num == 0) |
04e8dbaa | 502 | num = getKeyCounter(); |
6eb42cae | 503 | debug(20, 3, "storeGeneratePrivateKey: '%s'\n", url); |
504 | key_temp_buffer[0] = '\0'; | |
505 | sprintf(key_temp_buffer, "%d/%s/%s", | |
506 | num, | |
73ddce89 | 507 | RequestMethodStr[method], |
6eb42cae | 508 | url); |
509 | return key_temp_buffer; | |
510 | } | |
511 | ||
73ddce89 | 512 | char *storeGeneratePublicKey(url, method) |
090089c4 | 513 | char *url; |
73ddce89 | 514 | int method; |
090089c4 | 515 | { |
73ddce89 | 516 | debug(20, 3, "storeGeneratePublicKey: type=%d %s\n", method, url); |
517 | switch (method) { | |
6eb42cae | 518 | case METHOD_GET: |
227fbb74 | 519 | return url; |
520 | break; | |
6eb42cae | 521 | case METHOD_POST: |
090089c4 | 522 | sprintf(key_temp_buffer, "/post/%s", url); |
523 | return key_temp_buffer; | |
227fbb74 | 524 | break; |
6eb42cae | 525 | case METHOD_HEAD: |
090089c4 | 526 | sprintf(key_temp_buffer, "/head/%s", url); |
527 | return key_temp_buffer; | |
227fbb74 | 528 | break; |
3facc31e | 529 | case METHOD_CONNECT: |
530 | sprintf(key_temp_buffer, "/connect/%s", url); | |
531 | return key_temp_buffer; | |
532 | break; | |
227fbb74 | 533 | default: |
6eb42cae | 534 | fatal_dump("storeGeneratePublicKey: Unsupported request method"); |
227fbb74 | 535 | break; |
536 | } | |
537 | return NULL; | |
538 | } | |
539 | ||
6eb42cae | 540 | void storeSetPrivateKey(e) |
541 | StoreEntry *e; | |
227fbb74 | 542 | { |
6eb42cae | 543 | StoreEntry *e2 = NULL; |
544 | hash_link *table_entry = NULL; | |
07622ccd | 545 | char *newkey = NULL; |
227fbb74 | 546 | |
6eb42cae | 547 | if (e->key && BIT_TEST(e->flag, KEY_PRIVATE)) |
548 | return; /* is already private */ | |
090089c4 | 549 | |
73ddce89 | 550 | newkey = storeGeneratePrivateKey(e->url, e->method, 0); |
07622ccd | 551 | if ((table_entry = hash_lookup(table, newkey))) { |
552 | e2 = (StoreEntry *) table_entry; | |
553 | debug(20, 0, "storeSetPrivateKey: Entry already exists with key '%s'\n", | |
554 | newkey); | |
555 | debug(20, 0, "storeSetPrivateKey: Entry Dump:\n%s\n", storeToString(e2)); | |
556 | fatal_dump("Private key already exists."); | |
227fbb74 | 557 | } |
07622ccd | 558 | if (e->key) |
559 | storeHashDelete(e); | |
560 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) | |
561 | safe_free(e->key); | |
562 | e->key = xstrdup(newkey); | |
6eb42cae | 563 | storeHashInsert(e); |
227fbb74 | 564 | BIT_RESET(e->flag, KEY_URL); |
6eb42cae | 565 | BIT_SET(e->flag, KEY_CHANGE); |
566 | BIT_SET(e->flag, KEY_PRIVATE); | |
227fbb74 | 567 | } |
568 | ||
6eb42cae | 569 | void storeSetPublicKey(e) |
227fbb74 | 570 | StoreEntry *e; |
571 | { | |
6eb42cae | 572 | StoreEntry *e2 = NULL; |
573 | hash_link *table_entry = NULL; | |
07622ccd | 574 | char *newkey = NULL; |
6eb42cae | 575 | |
576 | if (e->key && !BIT_TEST(e->flag, KEY_PRIVATE)) | |
577 | return; /* is already public */ | |
578 | ||
73ddce89 | 579 | newkey = storeGeneratePublicKey(e->url, e->method); |
07622ccd | 580 | while ((table_entry = hash_lookup(table, newkey))) { |
581 | debug(20, 0, "storeSetPublicKey: Making old '%s' private.\n", newkey); | |
582 | e2 = (StoreEntry *) table_entry; | |
583 | storeSetPrivateKey(e2); | |
2daae136 | 584 | storeReleaseRequest(e2); |
6eb42cae | 585 | } |
07622ccd | 586 | if (e->key) |
587 | storeHashDelete(e); | |
588 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) | |
589 | safe_free(e->key); | |
73ddce89 | 590 | if (e->method == METHOD_GET) { |
6eb42cae | 591 | e->key = e->url; |
592 | BIT_SET(e->flag, KEY_URL); | |
593 | BIT_RESET(e->flag, KEY_CHANGE); | |
594 | } else { | |
07622ccd | 595 | e->key = xstrdup(newkey); |
6eb42cae | 596 | BIT_RESET(e->flag, KEY_URL); |
597 | BIT_SET(e->flag, KEY_CHANGE); | |
227fbb74 | 598 | } |
07622ccd | 599 | BIT_RESET(e->flag, KEY_PRIVATE); |
227fbb74 | 600 | storeHashInsert(e); |
601 | } | |
602 | ||
6eb42cae | 603 | StoreEntry *storeCreateEntry(url, req_hdr, flags, method) |
090089c4 | 604 | char *url; |
6eb42cae | 605 | char *req_hdr; |
606 | int flags; | |
607 | int method; | |
090089c4 | 608 | { |
090089c4 | 609 | StoreEntry *e = NULL; |
6eb42cae | 610 | MemObject *m = NULL; |
6602e70e | 611 | debug(20, 3, "storeCreateEntry: '%s' icp flags=%x\n", url, flags); |
090089c4 | 612 | |
613 | if (meta_data.hot_vm > store_hotobj_high) | |
614 | storeGetMemSpace(0, 1); | |
6eb42cae | 615 | e = new_StoreEntry(WITH_MEMOBJ); |
616 | m = e->mem_obj; | |
090089c4 | 617 | e->url = xstrdup(url); |
6eb42cae | 618 | meta_data.url_strings += strlen(url); |
73ddce89 | 619 | e->method = method; |
6eb42cae | 620 | if (req_hdr) |
621 | m->mime_hdr = xstrdup(req_hdr); | |
622 | if (BIT_TEST(flags, REQ_NOCACHE)) | |
623 | BIT_SET(e->flag, REFRESH_REQUEST); | |
624 | if (BIT_TEST(flags, REQ_PUBLIC)) { | |
090089c4 | 625 | BIT_SET(e->flag, CACHABLE); |
626 | BIT_RESET(e->flag, RELEASE_REQUEST); | |
6eb42cae | 627 | BIT_RESET(e->flag, ENTRY_PRIVATE); |
090089c4 | 628 | } else { |
629 | BIT_RESET(e->flag, CACHABLE); | |
2daae136 | 630 | storeReleaseRequest(e); |
6eb42cae | 631 | BIT_SET(e->flag, ENTRY_PRIVATE); |
090089c4 | 632 | } |
6eb42cae | 633 | if (neighbors_do_private_keys || !BIT_TEST(flags, REQ_PUBLIC)) |
634 | storeSetPrivateKey(e); | |
090089c4 | 635 | else |
6eb42cae | 636 | storeSetPublicKey(e); |
637 | if (BIT_TEST(flags, REQ_HTML)) | |
638 | BIT_SET(e->flag, ENTRY_HTML); | |
090089c4 | 639 | |
640 | e->status = STORE_PENDING; | |
641 | storeSetMemStatus(e, NOT_IN_MEMORY); | |
642 | e->swap_status = NO_SWAP; | |
643 | e->swap_file_number = -1; | |
644 | e->lock_count = 0; | |
6eb42cae | 645 | m->data = new_MemObjectData(); |
090089c4 | 646 | e->refcount = 0; |
647 | e->lastref = cached_curtime; | |
648 | e->timestamp = 0; /* set in storeSwapOutHandle() */ | |
649 | e->ping_status = NOPING; | |
227fbb74 | 650 | |
090089c4 | 651 | /* allocate pending list */ |
6eb42cae | 652 | m->pending_list_size = MIN_PENDING; |
653 | m->pending = (struct pentry **) | |
654 | xcalloc(m->pending_list_size, sizeof(struct pentry *)); | |
090089c4 | 655 | |
656 | /* allocate client list */ | |
6eb42cae | 657 | m->client_list_size = MIN_CLIENT; |
658 | m->client_list = (ClientStatusEntry **) | |
659 | xcalloc(m->client_list_size, sizeof(ClientStatusEntry *)); | |
2285407f | 660 | /* storeLog(STORE_LOG_CREATE, e); */ |
090089c4 | 661 | return e; |
6eb42cae | 662 | |
090089c4 | 663 | } |
664 | ||
665 | /* Add a new object to the cache with empty memory copy and pointer to disk | |
666 | * use to rebuild store from disk. */ | |
667 | StoreEntry *storeAddDiskRestore(url, file_number, size, expires, timestamp) | |
668 | char *url; | |
669 | int file_number; | |
670 | int size; | |
671 | time_t expires; | |
672 | time_t timestamp; | |
673 | { | |
674 | StoreEntry *e = NULL; | |
675 | ||
c943f331 | 676 | debug(20, 5, "StoreAddDiskRestore: <URL:%s>: size %d: expires %d: file_number %d\n", |
090089c4 | 677 | url, size, expires, file_number); |
678 | ||
3facc31e | 679 | /* if you call this you'd better be sure file_number is not |
680 | * already in use! */ | |
66a91d2d | 681 | |
090089c4 | 682 | meta_data.store_entries++; |
683 | meta_data.url_strings += strlen(url); | |
684 | ||
227fbb74 | 685 | e = new_StoreEntry(WITHOUT_MEMOBJ); |
090089c4 | 686 | e->url = xstrdup(url); |
6eb42cae | 687 | BIT_RESET(e->flag, ENTRY_PRIVATE); |
73ddce89 | 688 | e->method = METHOD_GET; |
6eb42cae | 689 | storeSetPublicKey(e); |
090089c4 | 690 | BIT_SET(e->flag, CACHABLE); |
691 | BIT_RESET(e->flag, RELEASE_REQUEST); | |
6eb42cae | 692 | BIT_SET(e->flag, ENTRY_HTML); |
090089c4 | 693 | e->status = STORE_OK; |
694 | storeSetMemStatus(e, NOT_IN_MEMORY); | |
695 | e->swap_status = SWAP_OK; | |
696 | e->swap_file_number = file_number; | |
697 | file_map_bit_set(file_number); | |
698 | e->object_len = size; | |
699 | e->lock_count = 0; | |
090089c4 | 700 | BIT_RESET(e->flag, CLIENT_ABORT_REQUEST); |
701 | e->refcount = 0; | |
702 | e->lastref = cached_curtime; | |
703 | e->timestamp = (u_num32) timestamp; | |
704 | e->expires = (u_num32) expires; | |
705 | e->ping_status = NOPING; | |
090089c4 | 706 | return e; |
707 | } | |
708 | ||
709 | /* Register interest in an object currently being retrieved. */ | |
710 | int storeRegister(e, fd, handler, data) | |
711 | StoreEntry *e; | |
712 | int fd; | |
713 | PIF handler; | |
2318883b | 714 | void *data; |
090089c4 | 715 | { |
73ddce89 | 716 | PendingEntry *pe = NULL; |
717 | int old_size; | |
718 | int i; | |
719 | int j; | |
090089c4 | 720 | |
2285407f | 721 | debug(20, 3, "storeRegister: FD %d '%s'\n", fd, e->key); |
090089c4 | 722 | |
73ddce89 | 723 | pe = (PendingEntry *) xcalloc(1, sizeof(PendingEntry)); |
090089c4 | 724 | pe->fd = fd; |
725 | pe->handler = handler; | |
726 | pe->data = data; | |
727 | ||
728 | /* | |
729 | * I've rewritten all this pendings stuff so that num_pending goes | |
730 | * away, and to fix all of the 'array bounds' problems we were having. | |
731 | * It's now a very simple array, with any NULL slot empty/avail. | |
732 | * If something needs to be added and there are no empty slots, | |
733 | * it'll grow the array. | |
734 | */ | |
735 | /* find an empty slot */ | |
22e4fa85 | 736 | for (i = 0; i < (int) e->mem_obj->pending_list_size; i++) |
737 | if (e->mem_obj->pending[i] == NULL) | |
090089c4 | 738 | break; |
739 | ||
22e4fa85 | 740 | if (i == e->mem_obj->pending_list_size) { |
090089c4 | 741 | /* grow the array */ |
742 | struct pentry **tmp = NULL; | |
743 | ||
22e4fa85 | 744 | old_size = e->mem_obj->pending_list_size; |
090089c4 | 745 | |
746 | /* set list_size to an appropriate amount */ | |
22e4fa85 | 747 | e->mem_obj->pending_list_size += MIN_PENDING; |
090089c4 | 748 | |
749 | /* allocate, and copy old pending list over to the new one */ | |
22e4fa85 | 750 | tmp = (struct pentry **) xcalloc(e->mem_obj->pending_list_size, |
090089c4 | 751 | sizeof(struct pentry *)); |
752 | for (j = 0; j < old_size; j++) | |
22e4fa85 | 753 | tmp[j] = e->mem_obj->pending[j]; |
090089c4 | 754 | |
755 | /* free the old list and set the new one */ | |
22e4fa85 | 756 | safe_free(e->mem_obj->pending); |
757 | e->mem_obj->pending = tmp; | |
090089c4 | 758 | |
c943f331 | 759 | debug(20, 10, "storeRegister: grew pending list to %d for slot %d.\n", |
22e4fa85 | 760 | e->mem_obj->pending_list_size, i); |
090089c4 | 761 | |
762 | } | |
22e4fa85 | 763 | e->mem_obj->pending[i] = pe; |
090089c4 | 764 | return 0; |
765 | } | |
766 | ||
767 | /* remove handler assoicate to that fd from store pending list */ | |
768 | /* Also remove entry from client_list if exist. */ | |
769 | /* return number of successfully free pending entries */ | |
770 | int storeUnregister(e, fd) | |
771 | StoreEntry *e; | |
772 | int fd; | |
773 | { | |
774 | int i; | |
775 | int freed = 0; | |
776 | ||
2285407f | 777 | debug(20, 10, "storeUnregister: called for FD %d '%s'\n", fd, e->key); |
090089c4 | 778 | |
779 | /* look for entry in client_list */ | |
22e4fa85 | 780 | if (e->mem_obj->client_list) { |
781 | for (i = 0; i < e->mem_obj->client_list_size; ++i) { | |
782 | if (e->mem_obj->client_list[i] && (e->mem_obj->client_list[i]->fd == fd)) { | |
090089c4 | 783 | /* reset fd to zero as a mark for empty slot */ |
22e4fa85 | 784 | safe_free(e->mem_obj->client_list[i]); |
785 | e->mem_obj->client_list[i] = NULL; | |
090089c4 | 786 | } |
787 | } | |
788 | } | |
789 | /* walk the entire list looking for matched fd */ | |
22e4fa85 | 790 | for (i = 0; i < (int) e->mem_obj->pending_list_size; i++) { |
791 | if (e->mem_obj->pending[i] && (e->mem_obj->pending[i]->fd == fd)) { | |
090089c4 | 792 | /* found the match fd */ |
22e4fa85 | 793 | safe_free(e->mem_obj->pending[i]); |
794 | e->mem_obj->pending[i] = NULL; | |
090089c4 | 795 | freed++; |
796 | } | |
797 | } | |
798 | ||
c943f331 | 799 | debug(20, 10, "storeUnregister: returning %d\n", freed); |
090089c4 | 800 | return freed; |
801 | } | |
802 | ||
803 | /* Call to delete behind upto "target lowest offset" | |
804 | * also, it update e_lowest_offset. | |
805 | */ | |
806 | void storeDeleteBehind(e) | |
807 | StoreEntry *e; | |
808 | { | |
809 | int free_up_to; | |
810 | int target_offset; | |
811 | int n_client = 0; | |
812 | int i; | |
813 | ||
c943f331 | 814 | debug(20, 3, "storeDeleteBehind: Object: %s\n", e->key); |
815 | debug(20, 3, "storeDeleteBehind:\tOriginal Lowest Offset: %d \n", e->mem_obj->e_lowest_offset); | |
090089c4 | 816 | |
22e4fa85 | 817 | free_up_to = e->mem_obj->e_lowest_offset; |
090089c4 | 818 | target_offset = 0; |
819 | ||
22e4fa85 | 820 | for (i = 0; i < e->mem_obj->client_list_size; ++i) { |
821 | if (e->mem_obj->client_list[i] == NULL) | |
090089c4 | 822 | continue; |
22e4fa85 | 823 | if (((e->mem_obj->client_list[i]->last_offset < target_offset) || |
090089c4 | 824 | (target_offset == 0))) { |
825 | n_client++; | |
22e4fa85 | 826 | target_offset = e->mem_obj->client_list[i]->last_offset; |
090089c4 | 827 | } |
828 | } | |
829 | ||
830 | if (n_client == 0) { | |
c943f331 | 831 | debug(20, 3, "storeDeleteBehind:\tThere is no client in the list.\n"); |
832 | debug(20, 3, "\t\tTry to delete as fast as possible.\n"); | |
22e4fa85 | 833 | target_offset = e->mem_obj->e_current_len; |
090089c4 | 834 | } |
c943f331 | 835 | debug(20, 3, "storeDeleteBehind:\tThe target offset is : %d\n", target_offset); |
090089c4 | 836 | if (target_offset) { |
22e4fa85 | 837 | free_up_to = (int) e->mem_obj->data->mem_free_data_upto(e->mem_obj->data, |
090089c4 | 838 | target_offset); |
c943f331 | 839 | debug(20, 3, " Object is freed upto : %d\n", free_up_to); |
22e4fa85 | 840 | store_mem_size -= free_up_to - e->mem_obj->e_lowest_offset; |
090089c4 | 841 | } |
c943f331 | 842 | debug(20, 3, "storeDeleteBehind:\tOutgoing Lowest Offset : %d\n", free_up_to); |
22e4fa85 | 843 | e->mem_obj->e_lowest_offset = free_up_to; |
090089c4 | 844 | } |
845 | ||
846 | /* Call handlers waiting for data to be appended to E. */ | |
847 | static void InvokeHandlers(e) | |
848 | StoreEntry *e; | |
849 | { | |
850 | int i; | |
851 | ||
852 | /* walk the entire list looking for valid handlers */ | |
22e4fa85 | 853 | for (i = 0; i < (int) e->mem_obj->pending_list_size; i++) { |
854 | if (e->mem_obj->pending[i] && e->mem_obj->pending[i]->handler) { | |
090089c4 | 855 | /* |
856 | * Once we call the handler, it is no longer needed | |
857 | * until the write process sends all available data | |
858 | * from the object entry. | |
859 | */ | |
22e4fa85 | 860 | (e->mem_obj->pending[i]->handler) |
861 | (e->mem_obj->pending[i]->fd, e, e->mem_obj->pending[i]->data); | |
862 | safe_free(e->mem_obj->pending[i]); | |
863 | e->mem_obj->pending[i] = NULL; | |
090089c4 | 864 | } |
865 | } | |
866 | ||
867 | } | |
868 | ||
6eb42cae | 869 | /* Mark object as expired */ |
9174e204 | 870 | void storeExpireNow(e) |
871 | StoreEntry *e; | |
872 | { | |
2285407f | 873 | debug(20, 3, "storeExpireNow: '%s'\n", e->key); |
9174e204 | 874 | e->expires = cached_curtime; |
875 | } | |
876 | ||
6eb42cae | 877 | /* switch object to deleting behind mode call by |
878 | * retrieval module when object gets too big. */ | |
090089c4 | 879 | void storeStartDeleteBehind(e) |
880 | StoreEntry *e; | |
881 | { | |
c943f331 | 882 | debug(20, 2, "storeStartDeleteBehind: Object: %s\n", e->key); |
090089c4 | 883 | if (e->flag & DELETE_BEHIND) { |
c943f331 | 884 | debug(20, 2, "storeStartDeleteBehind:\tis already in delete behind mode.\n"); |
090089c4 | 885 | return; |
886 | } | |
c943f331 | 887 | debug(20, 2, "storeStartDeleteBehind:\tis now in delete behind mode.\n"); |
090089c4 | 888 | /* change its key, so it couldn't be found by other client */ |
6eb42cae | 889 | storeSetPrivateKey(e); |
090089c4 | 890 | BIT_SET(e->flag, DELETE_BEHIND); |
2daae136 | 891 | storeReleaseRequest(e); |
090089c4 | 892 | BIT_RESET(e->flag, CACHABLE); |
9174e204 | 893 | storeExpireNow(e); |
090089c4 | 894 | } |
895 | ||
896 | /* Append incoming data from a primary server to an entry. */ | |
2aca8433 | 897 | void storeAppend(e, data, len) |
090089c4 | 898 | StoreEntry *e; |
899 | char *data; | |
900 | int len; | |
901 | { | |
902 | /* validity check -- sometimes it's called with bogus values */ | |
2aca8433 | 903 | if (e == NULL) |
904 | fatal_dump("storeAppend: NULL entry."); | |
905 | if (e->mem_obj == NULL) | |
906 | fatal_dump("storeAppend: NULL entry->mem_obj"); | |
907 | if (e->mem_obj->data == NULL) | |
908 | fatal_dump("storeAppend: NULL entry->mem_obj->data"); | |
909 | ||
090089c4 | 910 | if (len) { |
2285407f | 911 | debug(20, 5, "storeAppend: appending %d bytes for '%s'\n", len, e->key); |
090089c4 | 912 | |
913 | /* get some extra storage if needed */ | |
914 | (void) storeGetMemSpace(len, 0); | |
915 | store_mem_size += len; | |
c943f331 | 916 | debug(20, 8, "storeAppend: growing store_mem_size by %d\n", len); |
917 | debug(20, 8, "storeAppend: store_mem_size = %d\n", store_mem_size); | |
090089c4 | 918 | |
22e4fa85 | 919 | (void) e->mem_obj->data->mem_append(e->mem_obj->data, |
090089c4 | 920 | data, len); |
22e4fa85 | 921 | e->mem_obj->e_current_len += len; |
c943f331 | 922 | debug(20, 8, "storeAppend: e_current_len = %d\n", |
22e4fa85 | 923 | e->mem_obj->e_current_len); |
090089c4 | 924 | } |
925 | if ((e->status != STORE_ABORTED) && !(e->flag & DELAY_SENDING)) | |
926 | InvokeHandlers(e); | |
090089c4 | 927 | } |
928 | ||
929 | /* add directory to swap disk */ | |
930 | int storeAddSwapDisk(path) | |
931 | char *path; | |
932 | { | |
933 | if (cache_dirs == NULL) | |
934 | cache_dirs = create_dynamic_array(5, 5); | |
0ffd22bc | 935 | /* XXX note xstrdup here prob means we |
d68adf86 | 936 | * can't use destroy_dynamic_array() */ |
0ffd22bc | 937 | insert_dynamic_array(cache_dirs, xstrdup(path)); |
090089c4 | 938 | return ++ncache_dirs; |
939 | } | |
940 | ||
941 | /* return the nth swap directory */ | |
942 | char *swappath(n) | |
943 | int n; | |
944 | { | |
945 | return cache_dirs->collection[n % ncache_dirs]; | |
946 | } | |
947 | ||
948 | ||
949 | /* return full name to swapfile */ | |
950 | char *storeSwapFullPath(fn, fullpath) | |
951 | int fn; | |
952 | char *fullpath; | |
953 | { | |
954 | static char fullfilename[MAX_FILE_NAME_LEN]; | |
955 | ||
956 | if (fullpath) { | |
957 | sprintf(fullpath, "%s/%02d/%d", | |
958 | swappath(fn), | |
959 | (fn / ncache_dirs) % SWAP_DIRECTORIES, | |
960 | fn); | |
961 | return fullpath; | |
962 | } | |
963 | fullfilename[0] = '\0'; | |
964 | sprintf(fullfilename, "%s/%02d/%d", | |
965 | swappath(fn), | |
966 | (fn / ncache_dirs) % SWAP_DIRECTORIES, | |
967 | fn); | |
968 | return fullfilename; | |
969 | } | |
970 | ||
971 | /* swapping in handle */ | |
972 | int storeSwapInHandle(fd_notused, buf, len, flag, e, offset_notused) | |
973 | int fd_notused; | |
974 | char *buf; | |
975 | int len; | |
976 | int flag; | |
977 | StoreEntry *e; | |
978 | int offset_notused; | |
979 | { | |
2285407f | 980 | debug(20, 2, "storeSwapInHandle: '%s'\n", e->key); |
090089c4 | 981 | |
982 | if ((flag < 0) && (flag != DISK_EOF)) { | |
c943f331 | 983 | debug(20, 0, "storeSwapInHandle: SwapIn failure (err code = %d).\n", flag); |
2daae136 | 984 | put_free_8k_page(e->mem_obj->e_swap_buf); |
090089c4 | 985 | storeSetMemStatus(e, NOT_IN_MEMORY); |
22e4fa85 | 986 | file_close(e->mem_obj->swap_fd); |
090089c4 | 987 | swapInError(-1, e); /* Invokes storeAbort() and completes the I/O */ |
988 | return -1; | |
989 | } | |
c943f331 | 990 | debug(20, 5, "storeSwapInHandle: e->swap_offset = %d\n", |
22e4fa85 | 991 | e->mem_obj->swap_offset); |
c943f331 | 992 | debug(20, 5, "storeSwapInHandle: len = %d\n", |
090089c4 | 993 | len); |
c943f331 | 994 | debug(20, 5, "storeSwapInHandle: e->e_current_len = %d\n", |
22e4fa85 | 995 | e->mem_obj->e_current_len); |
c943f331 | 996 | debug(20, 5, "storeSwapInHandle: e->object_len = %d\n", |
090089c4 | 997 | e->object_len); |
998 | ||
999 | /* always call these, even if len == 0 */ | |
22e4fa85 | 1000 | e->mem_obj->swap_offset += len; |
090089c4 | 1001 | storeAppend(e, buf, len); |
1002 | ||
22e4fa85 | 1003 | if (e->mem_obj->e_current_len < e->object_len && flag != DISK_EOF) { |
090089c4 | 1004 | /* some more data to swap in, reschedule */ |
22e4fa85 | 1005 | file_read(e->mem_obj->swap_fd, |
1006 | e->mem_obj->e_swap_buf, | |
090089c4 | 1007 | SWAP_BUF, |
22e4fa85 | 1008 | e->mem_obj->swap_offset, |
090089c4 | 1009 | (FILE_READ_HD) storeSwapInHandle, |
51496678 | 1010 | (void *) e); |
090089c4 | 1011 | } else { |
1012 | /* complete swapping in */ | |
1013 | storeSetMemStatus(e, IN_MEMORY); | |
2daae136 | 1014 | put_free_8k_page(e->mem_obj->e_swap_buf); |
22e4fa85 | 1015 | file_close(e->mem_obj->swap_fd); |
2285407f | 1016 | storeLog(STORE_LOG_SWAPIN, e); |
c943f331 | 1017 | debug(20, 5, "storeSwapInHandle: SwapIn complete: <URL:%s> from %s.\n", |
090089c4 | 1018 | e->url, storeSwapFullPath(e->swap_file_number, NULL)); |
22e4fa85 | 1019 | if (e->mem_obj->e_current_len != e->object_len) { |
c943f331 | 1020 | debug(20, 0, "storeSwapInHandle: WARNING! Object size mismatch.\n"); |
1021 | debug(20, 0, " --> <URL:%s>\n", e->url); | |
1022 | debug(20, 0, " --> Expecting %d bytes from file: %s\n", e->object_len, | |
090089c4 | 1023 | storeSwapFullPath(e->swap_file_number, NULL)); |
c943f331 | 1024 | debug(20, 0, " --> Only read %d bytes\n", |
22e4fa85 | 1025 | e->mem_obj->e_current_len); |
090089c4 | 1026 | } |
1027 | if (e->flag & RELEASE_REQUEST) | |
1028 | storeRelease(e); | |
1029 | } | |
1030 | return 0; | |
1031 | } | |
1032 | ||
1033 | /* start swapping in */ | |
1034 | int storeSwapInStart(e) | |
1035 | StoreEntry *e; | |
1036 | { | |
1037 | int fd; | |
fc07a0e4 | 1038 | char *path = NULL; |
090089c4 | 1039 | |
1040 | /* sanity check! */ | |
1041 | if ((e->swap_status != SWAP_OK) || (e->swap_file_number < 0)) { | |
fc07a0e4 | 1042 | debug(20, 0, "storeSwapInStart: <No filename:%d> ? <URL:%s>\n", |
1043 | e->swap_file_number, e->url); | |
2aca8433 | 1044 | if (e->mem_obj) |
22e4fa85 | 1045 | e->mem_obj->swap_fd = -1; |
090089c4 | 1046 | return -1; |
1047 | } | |
1048 | /* create additional structure for object in memory */ | |
227fbb74 | 1049 | e->mem_obj = new_MemObject(); |
090089c4 | 1050 | |
fc07a0e4 | 1051 | path = storeSwapFullPath(e->swap_file_number, NULL); |
0ffd22bc | 1052 | if ((fd = file_open(path, NULL, O_RDONLY)) < 0) { |
fc07a0e4 | 1053 | debug(20, 0, "storeSwapInStart: Unable to open swapfile: %s\n", |
1054 | path); | |
1055 | debug(20, 0, "storeSwapInStart: --> for <URL:%s>\n", | |
1056 | e->url); | |
090089c4 | 1057 | storeSetMemStatus(e, NOT_IN_MEMORY); |
fc07a0e4 | 1058 | /* Invoke a store abort that should free the memory object */ |
090089c4 | 1059 | return -1; |
1060 | } | |
0ffd22bc | 1061 | e->mem_obj->swap_fd = (short) fd; |
c943f331 | 1062 | debug(20, 5, "storeSwapInStart: initialized swap file '%s' for <URL:%s>\n", |
fc07a0e4 | 1063 | path, e->url); |
090089c4 | 1064 | |
1065 | storeSetMemStatus(e, SWAPPING_IN); | |
fc07a0e4 | 1066 | e->mem_obj->data = new_MemObjectData(); |
22e4fa85 | 1067 | e->mem_obj->swap_offset = 0; |
2daae136 | 1068 | e->mem_obj->e_swap_buf = get_free_8k_page(); |
090089c4 | 1069 | |
1070 | /* start swapping daemon */ | |
fc07a0e4 | 1071 | file_read(fd, |
22e4fa85 | 1072 | e->mem_obj->e_swap_buf, |
090089c4 | 1073 | SWAP_BUF, |
22e4fa85 | 1074 | e->mem_obj->swap_offset, |
090089c4 | 1075 | (FILE_READ_HD) storeSwapInHandle, |
51496678 | 1076 | (void *) e); |
090089c4 | 1077 | return 0; |
1078 | } | |
1079 | ||
1080 | void storeSwapOutHandle(fd, flag, e) | |
1081 | int fd; | |
1082 | int flag; | |
1083 | StoreEntry *e; | |
1084 | { | |
1085 | static char filename[MAX_FILE_NAME_LEN]; | |
090089c4 | 1086 | char *page_ptr = NULL; |
1087 | ||
2285407f | 1088 | debug(20, 3, "storeSwapOutHandle: '%s'\n", e->key); |
090089c4 | 1089 | |
1090 | e->timestamp = cached_curtime; | |
1091 | storeSwapFullPath(e->swap_file_number, filename); | |
22e4fa85 | 1092 | page_ptr = e->mem_obj->e_swap_buf; |
090089c4 | 1093 | |
1094 | if (flag < 0) { | |
c943f331 | 1095 | debug(20, 1, "storeSwapOutHandle: SwapOut failure (err code = %d).\n", |
090089c4 | 1096 | flag); |
1097 | e->swap_status = NO_SWAP; | |
2daae136 | 1098 | put_free_8k_page(page_ptr); |
090089c4 | 1099 | file_close(fd); |
2daae136 | 1100 | storeReleaseRequest(e); |
090089c4 | 1101 | if (e->swap_file_number != -1) { |
1102 | file_map_bit_reset(e->swap_file_number); | |
1103 | safeunlink(filename, 0); /* remove it */ | |
1104 | e->swap_file_number = -1; | |
1105 | } | |
1106 | if (flag == DISK_NO_SPACE_LEFT) { | |
1107 | /* reduce the swap_size limit to the current size. */ | |
1108 | setCacheSwapMax(store_swap_size); | |
1109 | store_swap_high = (long) (((float) getCacheSwapMax() * | |
1110 | (float) getCacheSwapHighWaterMark()) / (float) 100); | |
1111 | store_swap_low = (long) (((float) getCacheSwapMax() * | |
1112 | (float) getCacheSwapLowWaterMark()) / (float) 100); | |
1113 | } | |
1114 | return; | |
1115 | } | |
c943f331 | 1116 | debug(20, 6, "storeSwapOutHandle: e->swap_offset = %d\n", |
22e4fa85 | 1117 | e->mem_obj->swap_offset); |
c943f331 | 1118 | debug(20, 6, "storeSwapOutHandle: e->e_swap_buf_len = %d\n", |
22e4fa85 | 1119 | e->mem_obj->e_swap_buf_len); |
c943f331 | 1120 | debug(20, 6, "storeSwapOutHandle: e->object_len = %d\n", |
090089c4 | 1121 | e->object_len); |
c943f331 | 1122 | debug(20, 6, "storeSwapOutHandle: store_swap_size = %dk\n", |
090089c4 | 1123 | store_swap_size); |
1124 | ||
22e4fa85 | 1125 | e->mem_obj->swap_offset += e->mem_obj->e_swap_buf_len; |
090089c4 | 1126 | /* round up */ |
22e4fa85 | 1127 | store_swap_size += ((e->mem_obj->e_swap_buf_len + 1023) >> 10); |
1128 | if (e->mem_obj->swap_offset >= e->object_len) { | |
090089c4 | 1129 | /* swapping complete */ |
1130 | e->swap_status = SWAP_OK; | |
22e4fa85 | 1131 | file_close(e->mem_obj->swap_fd); |
2285407f | 1132 | storeLog(STORE_LOG_SWAPOUT, e); |
c943f331 | 1133 | debug(20, 5, "storeSwapOutHandle: SwapOut complete: <URL:%s> to %s.\n", |
090089c4 | 1134 | e->url, storeSwapFullPath(e->swap_file_number, NULL)); |
2daae136 | 1135 | put_free_8k_page(page_ptr); |
c943f331 | 1136 | sprintf(logmsg, "%s %s %d %d %d\n", |
090089c4 | 1137 | filename, |
1138 | e->url, | |
1139 | (int) e->expires, | |
1140 | (int) e->timestamp, | |
1141 | e->object_len); | |
1142 | /* Automatically freed by file_write because no-handlers */ | |
1143 | file_write(swaplog_fd, | |
1144 | xstrdup(logmsg), | |
1145 | strlen(logmsg), | |
1146 | swaplog_lock, | |
1147 | NULL, | |
1148 | NULL); | |
1149 | /* check if it's request to be released. */ | |
1150 | if (e->flag & RELEASE_REQUEST) | |
1151 | storeRelease(e); | |
1152 | return; | |
1153 | } | |
1154 | /* write some more data, reschedule itself. */ | |
3facc31e | 1155 | storeCopy(e, |
1156 | e->mem_obj->swap_offset, | |
1157 | SWAP_BUF, | |
1158 | e->mem_obj->e_swap_buf, | |
1159 | &(e->mem_obj->e_swap_buf_len)); | |
1160 | file_write(e->mem_obj->swap_fd, | |
1161 | e->mem_obj->e_swap_buf, | |
1162 | e->mem_obj->e_swap_buf_len, | |
1163 | e->mem_obj->e_swap_access, | |
1164 | storeSwapOutHandle, | |
1165 | e); | |
090089c4 | 1166 | return; |
1167 | ||
1168 | } | |
1169 | ||
1170 | ||
1171 | /* start swapping object to disk */ | |
07622ccd | 1172 | static int storeSwapOutStart(e) |
090089c4 | 1173 | StoreEntry *e; |
1174 | { | |
1175 | int fd; | |
1176 | static char swapfilename[MAX_FILE_NAME_LEN]; | |
1177 | ||
1178 | /* Suggest a new swap file number */ | |
1179 | swapfileno = (swapfileno + 1) % (MAX_SWAP_FILE); | |
1180 | /* Record the number returned */ | |
1181 | swapfileno = file_map_allocate(swapfileno); | |
1182 | storeSwapFullPath(swapfileno, swapfilename); | |
1183 | ||
1184 | fd = file_open(swapfilename, NULL, O_RDWR | O_CREAT | O_TRUNC); | |
1185 | if (fd < 0) { | |
c943f331 | 1186 | debug(20, 0, "storeSwapOutStart: Unable to open swapfile: %s\n", |
090089c4 | 1187 | swapfilename); |
1188 | file_map_bit_reset(swapfileno); | |
1189 | e->swap_file_number = -1; | |
1190 | return -1; | |
1191 | } | |
0ffd22bc | 1192 | e->mem_obj->swap_fd = (short) fd; |
c943f331 | 1193 | debug(20, 5, "storeSwapOutStart: Begin SwapOut <URL:%s> to FD %d FILE %s.\n", |
090089c4 | 1194 | e->url, fd, swapfilename); |
1195 | ||
1196 | e->swap_file_number = swapfileno; | |
22e4fa85 | 1197 | if ((e->mem_obj->e_swap_access = file_write_lock(e->mem_obj->swap_fd)) < 0) { |
c943f331 | 1198 | debug(20, 0, "storeSwapOutStart: Unable to lock swapfile: %s\n", |
090089c4 | 1199 | swapfilename); |
1200 | file_map_bit_reset(e->swap_file_number); | |
1201 | e->swap_file_number = -1; | |
1202 | return -1; | |
1203 | } | |
1204 | e->swap_status = SWAPPING_OUT; | |
22e4fa85 | 1205 | e->mem_obj->swap_offset = 0; |
2daae136 | 1206 | e->mem_obj->e_swap_buf = get_free_8k_page(); |
22e4fa85 | 1207 | e->mem_obj->e_swap_buf_len = 0; |
090089c4 | 1208 | |
22e4fa85 | 1209 | storeCopy(e, 0, SWAP_BUF, e->mem_obj->e_swap_buf, |
1210 | &(e->mem_obj->e_swap_buf_len)); | |
090089c4 | 1211 | |
1212 | /* start swapping daemon */ | |
22e4fa85 | 1213 | if (file_write(e->mem_obj->swap_fd, |
1214 | e->mem_obj->e_swap_buf, | |
1215 | e->mem_obj->e_swap_buf_len, | |
1216 | e->mem_obj->e_swap_access, | |
090089c4 | 1217 | storeSwapOutHandle, |
1218 | e) != DISK_OK) { | |
1219 | /* This shouldn't happen */ | |
1220 | fatal_dump(NULL); | |
1221 | } | |
1222 | return 0; | |
1223 | } | |
1224 | ||
1225 | /* recreate meta data from disk image in swap directory */ | |
3facc31e | 1226 | |
1227 | /* Add one swap file at a time from disk storage */ | |
1228 | static int storeDoRebuildFromDisk(data) | |
1229 | struct storeRebuild_data *data; | |
1230 | { | |
090089c4 | 1231 | static char log_swapfile[1024]; |
1232 | static char swapfile[1024]; | |
1233 | static char url[MAX_URL]; | |
1234 | char *t = NULL; | |
1235 | StoreEntry *e = NULL; | |
090089c4 | 1236 | time_t expires; |
1237 | time_t timestamp; | |
090089c4 | 1238 | int scan1, scan2, scan3; |
3facc31e | 1239 | struct stat sb; |
1240 | off_t size; | |
090089c4 | 1241 | int delta; |
090089c4 | 1242 | int sfileno = 0; |
090089c4 | 1243 | |
3facc31e | 1244 | if (!fgets(data->line_in, 4095, data->log)) |
1245 | return 0; | |
227fbb74 | 1246 | |
66a91d2d | 1247 | if ((data->linecount++ & 0xFFF) == 0) |
3facc31e | 1248 | debug(20, 1, " %7d Lines read so far.\n", data->linecount); |
090089c4 | 1249 | |
3facc31e | 1250 | debug(20, 10, "line_in: %s", data->line_in); |
1251 | if ((data->line_in[0] == '\0') || (data->line_in[0] == '\n') || | |
1252 | (data->line_in[0] == '#')) | |
1253 | return 1; /* skip bad lines */ | |
090089c4 | 1254 | |
3facc31e | 1255 | url[0] = log_swapfile[0] = '\0'; |
1256 | expires = cached_curtime; | |
090089c4 | 1257 | |
3facc31e | 1258 | scan3 = 0; |
1259 | size = 0; | |
1260 | if (sscanf(data->line_in, "%s %s %d %d %d", | |
1261 | log_swapfile, url, &scan1, &scan2, &scan3) != 5) { | |
1262 | if (opt_unlink_on_reload && log_swapfile[0]) | |
1263 | safeunlink(log_swapfile, 0); | |
1264 | return 1; | |
1265 | } | |
1266 | expires = (time_t) scan1; | |
1267 | timestamp = (time_t) scan2; | |
1268 | size = (off_t) scan3; | |
1269 | if ((t = strrchr(log_swapfile, '/'))) | |
1270 | sfileno = atoi(t + 1); | |
1271 | else | |
1272 | sfileno = atoi(log_swapfile); | |
1273 | storeSwapFullPath(sfileno, swapfile); | |
090089c4 | 1274 | |
3facc31e | 1275 | /* |
1276 | * Note that swapfile may be different than log_swapfile if | |
1277 | * another cache_dir is added. | |
1278 | */ | |
090089c4 | 1279 | |
3facc31e | 1280 | if (!data->fast_mode) { |
1281 | if (stat(swapfile, &sb) < 0) { | |
1282 | if (expires < cached_curtime) { | |
1283 | debug(20, 3, "storeRebuildFromDisk: Expired: <URL:%s>\n", url); | |
1284 | if (opt_unlink_on_reload) | |
1285 | safeunlink(swapfile, 1); | |
1286 | data->expcount++; | |
1287 | } else { | |
1288 | debug(20, 3, "storeRebuildFromDisk: Swap file missing: <URL:%s>: %s: %s.\n", url, swapfile, xstrerror()); | |
30731487 | 1289 | if (opt_unlink_on_reload) |
d68adf86 | 1290 | safeunlink(log_swapfile, 1); |
090089c4 | 1291 | } |
3facc31e | 1292 | return 1; |
090089c4 | 1293 | } |
3facc31e | 1294 | /* Empty swap file? */ |
1295 | if (sb.st_size == 0) { | |
1296 | if (opt_unlink_on_reload) | |
1297 | safeunlink(log_swapfile, 1); | |
1298 | return 1; | |
1299 | } | |
1300 | /* timestamp might be a little bigger than sb.st_mtime */ | |
1301 | delta = (int) (timestamp - sb.st_mtime); | |
1302 | if (delta > REBUILD_TIMESTAMP_DELTA_MAX || delta < 0) { | |
1303 | /* this log entry doesn't correspond to this file */ | |
1304 | data->clashcount++; | |
1305 | return 1; | |
1306 | } | |
1307 | /* Wrong size? */ | |
1308 | if (sb.st_size != size) { | |
1309 | /* this log entry doesn't correspond to this file */ | |
1310 | data->clashcount++; | |
1311 | return 1; | |
090089c4 | 1312 | } |
3facc31e | 1313 | timestamp = sb.st_mtime; |
1314 | debug(20, 10, "storeRebuildFromDisk: Cached file exists: <URL:%s>: %s\n", | |
1315 | url, swapfile); | |
1316 | } | |
1317 | if ((e = storeGet(url))) { | |
1318 | if (e->timestamp > timestamp) { | |
1319 | /* already have a newer object in memory, throw old one away */ | |
1320 | debug(20, 3, "storeRebuildFromDisk: Replaced: %s\n", url); | |
30731487 | 1321 | if (opt_unlink_on_reload) |
d68adf86 | 1322 | safeunlink(swapfile, 1); |
3facc31e | 1323 | data->dupcount++; |
1324 | return 1; | |
090089c4 | 1325 | } |
3facc31e | 1326 | debug(20, 6, "storeRebuildFromDisk: Duplicate: <URL:%s>\n", url); |
1327 | storeRelease(e); | |
1328 | data->objcount--; | |
1329 | data->dupcount++; | |
1330 | } | |
1331 | if (expires < cached_curtime) { | |
1332 | debug(20, 3, "storeRebuildFromDisk: Expired: <URL:%s>\n", url); | |
1333 | if (opt_unlink_on_reload) | |
1334 | safeunlink(swapfile, 1); | |
1335 | data->expcount++; | |
1336 | return 1; | |
1337 | } | |
1338 | /* Is the swap file number already taken? */ | |
1339 | if (file_map_bit_test(sfileno)) { | |
1340 | /* Yes is is, we can't use this swapfile */ | |
1341 | debug(20, 1, "storeRebuildFromDisk: Active clash: file #%d\n", | |
1342 | sfileno); | |
1343 | debug(20, 3, "storeRebuildFromDisk: --> <URL:%s>\n", url); | |
1344 | if (opt_unlink_on_reload) | |
1345 | safeunlink(swapfile, 1); | |
1346 | data->clashcount++; | |
1347 | return 1; | |
c943f331 | 1348 | } |
3facc31e | 1349 | /* update store_swap_size */ |
1350 | store_swap_size += (int) ((size + 1023) >> 10); | |
1351 | data->objcount++; | |
1352 | ||
1353 | sprintf(logmsg, "%s %s %d %d %d\n", | |
1354 | swapfile, | |
1355 | url, | |
1356 | (int) expires, | |
1357 | (int) timestamp, | |
1358 | (int) size); | |
1359 | /* Automatically freed by file_write because no-handlers */ | |
1360 | file_write(swaplog_fd, | |
1361 | xstrdup(logmsg), | |
1362 | strlen(logmsg), | |
1363 | swaplog_lock, | |
1364 | NULL, | |
1365 | NULL); | |
1366 | storeAddDiskRestore(url, | |
1367 | sfileno, | |
1368 | (int) size, | |
1369 | expires, | |
1370 | timestamp); | |
1371 | CacheInfo->proto_newobject(CacheInfo, | |
1372 | CacheInfo->proto_id(url), | |
1373 | (int) size, | |
1374 | TRUE); | |
1375 | ||
1376 | return 1; | |
1377 | } | |
1378 | ||
1379 | /* meta data recreated from disk image in swap directory */ | |
1380 | static void storeRebuiltFromDisk(data) | |
1381 | struct storeRebuild_data *data; | |
1382 | { | |
1383 | time_t r; | |
1384 | time_t stop; | |
c943f331 | 1385 | |
b37a37f9 | 1386 | stop = getCurrentTime(); |
3facc31e | 1387 | r = stop - data->start; |
c943f331 | 1388 | debug(20, 1, "Finished rebuilding storage from disk image.\n"); |
3facc31e | 1389 | debug(20, 1, " %7d Lines read from previous logfile.\n", data->linecount); |
1390 | debug(20, 1, " %7d Objects loaded.\n", data->objcount); | |
1391 | debug(20, 1, " %7d Objects expired.\n", data->expcount); | |
1392 | debug(20, 1, " %7d Duplicate URLs purged.\n", data->dupcount); | |
1393 | debug(20, 1, " %7d Swapfile clashes avoided.\n", data->clashcount); | |
c943f331 | 1394 | debug(20, 1, " Took %d seconds (%6.1lf objects/sec).\n", |
3facc31e | 1395 | r > 0 ? r : 0, (double) data->objcount / (r > 0 ? r : 1)); |
c943f331 | 1396 | debug(20, 1, " store_swap_size = %dk\n", store_swap_size); |
090089c4 | 1397 | |
3facc31e | 1398 | ok_write_clean_log = 1; |
1399 | ||
1400 | fclose(data->log); | |
1401 | safe_free(data); | |
1402 | sprintf(tmp_filename, "%s.new", swaplog_file); | |
1403 | if (rename(tmp_filename, swaplog_file) < 0) { | |
1404 | debug(20, 0, "storeRebuiltFromDisk: rename failed: %s\n", | |
1405 | xstrerror()); | |
1406 | } | |
1407 | file_update_open(swaplog_fd, swaplog_file); | |
090089c4 | 1408 | } |
1409 | ||
3facc31e | 1410 | void storeStartRebuildFromDisk() |
1411 | { | |
1412 | struct stat sb; | |
1413 | int i; | |
1414 | struct storeRebuild_data *data; | |
1415 | time_t last_clean; | |
1416 | ||
1417 | if (stat(swaplog_file, &sb) < 0) { | |
1418 | debug(20, 1, "storeRebuildFromDisk: No log file\n"); | |
1419 | ok_write_clean_log = 1; | |
1420 | return; | |
1421 | } | |
1422 | data = xcalloc(1, sizeof(*data)); | |
1423 | ||
1424 | for (i = 0; i < ncache_dirs; ++i) | |
1425 | debug(20, 1, "Rebuilding storage from disk image in %s\n", swappath(i)); | |
1426 | data->start = getCurrentTime(); | |
1427 | ||
1428 | /* Check if log is clean */ | |
1429 | sprintf(tmp_filename, "%s/log-last-clean", swappath(0)); | |
1430 | if (stat(tmp_filename, &sb) >= 0) { | |
1431 | last_clean = sb.st_mtime; | |
1432 | sprintf(tmp_filename, "%s/log", swappath(0)); | |
1433 | if (stat(tmp_filename, &sb) >= 0) { | |
1434 | data->fast_mode = (sb.st_mtime <= last_clean) ? 1 : 0; | |
1435 | } | |
1436 | } | |
1437 | /* close the existing write-only swaplog, and open a temporary | |
1438 | * write-only swaplog */ | |
1439 | if (file_write_unlock(swaplog_fd, swaplog_lock) != DISK_OK) | |
1440 | fatal_dump("storeStartRebuildFromDisk: swaplog unlock failed"); | |
1441 | if (swaplog_fd > -1) | |
1442 | file_close(swaplog_fd); | |
1443 | sprintf(tmp_filename, "%s.new", swaplog_file); | |
1444 | swaplog_fd = file_open(tmp_filename, NULL, O_WRONLY | O_CREAT | O_APPEND); | |
66a91d2d | 1445 | debug(20, 3, "swaplog_fd %d is now '%s'\n", swaplog_fd, tmp_filename); |
3facc31e | 1446 | if (swaplog_fd < 0) { |
1447 | debug(20, 0, "storeStartRebuildFromDisk: %s: %s\n", | |
1448 | tmp_filename, xstrerror()); | |
1449 | fatal("storeStartRebuildFromDisk: Can't open tmp swaplog"); | |
1450 | } | |
1451 | swaplog_lock = file_write_lock(swaplog_fd); | |
66a91d2d | 1452 | debug(20, 3, "swaplog_lock = %d\n", swaplog_lock); |
3facc31e | 1453 | /* Open the existing swap log for reading */ |
1454 | if ((data->log = fopen(swaplog_file, "r")) == (FILE *) NULL) { | |
1455 | sprintf(tmp_error_buf, "storeRebuildFromDisk: %s: %s", | |
1456 | swaplog_file, xstrerror()); | |
1457 | fatal(tmp_error_buf); | |
1458 | } | |
66a91d2d | 1459 | debug(20, 3, "data->log %d is now '%s'\n", fileno(data->log), swaplog_file); |
3facc31e | 1460 | if (data->fast_mode) |
1461 | debug(20, 1, "Rebuilding in FAST MODE.\n"); | |
1462 | ||
1463 | memset(data->line_in, '\0', 4096); | |
1464 | ||
1465 | /* Start reading the log file */ | |
1466 | runInBackground("storeRebuild", | |
1467 | storeDoRebuildFromDisk, | |
1468 | data, | |
1469 | storeRebuiltFromDisk); | |
1470 | } | |
090089c4 | 1471 | |
1472 | /* return current swap size in kilo-bytes */ | |
1473 | int storeGetSwapSize() | |
1474 | { | |
1475 | return store_swap_size; | |
1476 | } | |
1477 | ||
1478 | /* return current swap size in bytes */ | |
1479 | int storeGetMemSize() | |
1480 | { | |
1481 | return store_mem_size; | |
1482 | } | |
1483 | ||
6602e70e | 1484 | static int storeCheckSwapable(e) |
1485 | StoreEntry *e; | |
1486 | { | |
1487 | ||
1488 | if (BIT_TEST(e->flag, ENTRY_PRIVATE)) { | |
2285407f | 1489 | debug(20, 2, "storeCheckSwapable: NO: private entry\n"); |
6602e70e | 1490 | } else if (e->expires <= cached_curtime) { |
2285407f | 1491 | debug(20, 2, "storeCheckSwapable: NO: already expired\n"); |
73ddce89 | 1492 | } else if (e->method != METHOD_GET) { |
2285407f | 1493 | debug(20, 2, "storeCheckSwapable: NO: non-GET method\n"); |
6602e70e | 1494 | } else if (!BIT_TEST(e->flag, CACHABLE)) { |
2285407f | 1495 | debug(20, 2, "storeCheckSwapable: NO: not cachable\n"); |
6602e70e | 1496 | } else if (BIT_TEST(e->flag, RELEASE_REQUEST)) { |
2285407f | 1497 | debug(20, 2, "storeCheckSwapable: NO: release requested\n"); |
6602e70e | 1498 | } else if (!storeEntryValidLength(e)) { |
2285407f | 1499 | debug(20, 2, "storeCheckSwapable: NO: wrong content-length\n"); |
6602e70e | 1500 | } else |
1501 | return 1; | |
1502 | ||
2daae136 | 1503 | storeReleaseRequest(e); |
6602e70e | 1504 | BIT_RESET(e->flag, CACHABLE); |
1505 | return 0; | |
1506 | } | |
1507 | ||
1508 | ||
090089c4 | 1509 | |
1510 | /* Complete transfer into the local cache. */ | |
1511 | void storeComplete(e) | |
1512 | StoreEntry *e; | |
1513 | { | |
2285407f | 1514 | debug(20, 3, "storeComplete: '%s'\n", e->key); |
090089c4 | 1515 | |
22e4fa85 | 1516 | e->object_len = e->mem_obj->e_current_len; |
090089c4 | 1517 | InvokeHandlers(e); |
1518 | e->lastref = cached_curtime; | |
1519 | e->status = STORE_OK; | |
1520 | storeSetMemStatus(e, IN_MEMORY); | |
1521 | e->swap_status = NO_SWAP; | |
6602e70e | 1522 | if (storeCheckSwapable(e)) |
090089c4 | 1523 | storeSwapOutStart(e); |
090089c4 | 1524 | /* free up incoming MIME */ |
22e4fa85 | 1525 | safe_free(e->mem_obj->mime_hdr); |
090089c4 | 1526 | CacheInfo->proto_newobject(CacheInfo, CacheInfo->proto_id(e->url), |
1527 | e->object_len, FALSE); | |
1528 | if (e->flag & RELEASE_REQUEST) | |
1529 | storeRelease(e); | |
1530 | } | |
1531 | ||
1532 | /* | |
1533 | * Fetch aborted. Tell all clients to go home. Negatively cache | |
1534 | * abort message, freeing the data for this object | |
1535 | */ | |
1536 | int storeAbort(e, msg) | |
1537 | StoreEntry *e; | |
1538 | char *msg; | |
1539 | { | |
1540 | static char mime_hdr[300]; | |
1541 | static char abort_msg[2000]; | |
1542 | ||
2285407f | 1543 | debug(20, 6, "storeAbort: '%s'\n", e->key); |
090089c4 | 1544 | e->expires = cached_curtime + getNegativeTTL(); |
1545 | e->status = STORE_ABORTED; | |
1546 | storeSetMemStatus(e, IN_MEMORY); | |
1547 | /* No DISK swap for negative cached object */ | |
1548 | e->swap_status = NO_SWAP; | |
1549 | e->lastref = cached_curtime; | |
1550 | /* In case some parent responds late and | |
1551 | * tries to restart the fetch, say that it's been | |
1552 | * dispatched already. | |
1553 | */ | |
6eb42cae | 1554 | BIT_SET(e->flag, ENTRY_DISPATCHED); |
090089c4 | 1555 | |
1556 | storeLockObject(e); | |
1557 | ||
1558 | /* Count bytes faulted through cache but not moved to disk */ | |
1559 | CacheInfo->proto_touchobject(CacheInfo, CacheInfo->proto_id(e->url), | |
22e4fa85 | 1560 | e->mem_obj->e_current_len); |
090089c4 | 1561 | CacheInfo->proto_touchobject(CacheInfo, CacheInfo->proto_id("abort:"), |
22e4fa85 | 1562 | e->mem_obj->e_current_len); |
090089c4 | 1563 | |
1564 | mk_mime_hdr(mime_hdr, | |
1565 | (time_t) getNegativeTTL(), | |
1566 | 6 + strlen(msg), | |
1567 | cached_curtime, | |
1568 | "text/html"); | |
1569 | if (msg) { | |
1570 | /* This can run off the end here. Be careful */ | |
1571 | if ((int) (strlen(msg) + strlen(mime_hdr) + 50) < 2000) { | |
1572 | sprintf(abort_msg, "HTTP/1.0 400 Cache Detected Error\r\n%s\r\n\r\n%s", mime_hdr, msg); | |
1573 | } else { | |
c943f331 | 1574 | debug(20, 0, "storeAbort: WARNING: Must increase msg length!"); |
090089c4 | 1575 | } |
1576 | storeAppend(e, abort_msg, strlen(abort_msg)); | |
22e4fa85 | 1577 | e->mem_obj->e_abort_msg = xstrdup(abort_msg); |
090089c4 | 1578 | /* Set up object for negative caching */ |
1579 | BIT_SET(e->flag, ABORT_MSG_PENDING); | |
1580 | } | |
1581 | /* We assign an object length here--The only other place we assign the | |
1582 | * object length is in storeComplete() */ | |
22e4fa85 | 1583 | e->object_len = e->mem_obj->e_current_len; |
090089c4 | 1584 | |
1585 | /* Call handlers so they can report error. */ | |
1586 | InvokeHandlers(e); | |
1587 | ||
1588 | storeUnlockObject(e); | |
1589 | return 0; | |
1590 | } | |
1591 | ||
1592 | /* get the first in memory object entry in the storage */ | |
1593 | hash_link *storeFindFirst(id) | |
1594 | HashID id; | |
1595 | { | |
1596 | if (id == (HashID) 0) | |
1597 | return NULL; | |
1598 | return (hash_first(id)); | |
1599 | } | |
1600 | ||
1601 | /* get the next in memory object entry in the storage for a given | |
1602 | * search pointer */ | |
1603 | hash_link *storeFindNext(id) | |
1604 | HashID id; | |
1605 | { | |
1606 | if (id == (HashID) 0) | |
1607 | return NULL; | |
1608 | return (hash_next(id)); | |
1609 | } | |
1610 | ||
1611 | /* get the first in memory object entry in the storage */ | |
1612 | StoreEntry *storeGetInMemFirst() | |
1613 | { | |
1614 | hash_link *first = NULL; | |
1615 | first = storeFindFirst(in_mem_table); | |
1616 | return (first ? ((StoreEntry *) first->item) : NULL); | |
1617 | } | |
1618 | ||
1619 | ||
1620 | /* get the next in memory object entry in the storage for a given | |
1621 | * search pointer */ | |
1622 | StoreEntry *storeGetInMemNext() | |
1623 | { | |
1624 | hash_link *next = NULL; | |
1625 | next = storeFindNext(in_mem_table); | |
1626 | return (next ? ((StoreEntry *) next->item) : NULL); | |
1627 | } | |
1628 | ||
1629 | /* get the first entry in the storage */ | |
1630 | StoreEntry *storeGetFirst() | |
1631 | { | |
1632 | return ((StoreEntry *) storeFindFirst(table)); | |
1633 | } | |
1634 | ||
1635 | ||
1636 | /* get the next entry in the storage for a given search pointer */ | |
1637 | StoreEntry *storeGetNext() | |
1638 | { | |
1639 | return ((StoreEntry *) storeFindNext(table)); | |
1640 | } | |
1641 | ||
1642 | ||
1643 | ||
1644 | /* walk through every single entry in the storage and invoke a given routine */ | |
1645 | int storeWalkThrough(proc, data) | |
2318883b | 1646 | int (*proc) _PARAMS((StoreEntry * e, void *data)); |
1647 | void *data; | |
090089c4 | 1648 | { |
1649 | StoreEntry *e = NULL; | |
1650 | int count = 0; | |
1651 | int n = 0; | |
1652 | ||
1653 | for (e = storeGetFirst(); e; e = storeGetNext()) { | |
1654 | if ((++n & 0xFF) == 0) | |
b37a37f9 | 1655 | getCurrentTime(); |
090089c4 | 1656 | if ((n & 0xFFF) == 0) |
c943f331 | 1657 | debug(20, 2, "storeWalkThrough: %7d objects so far.\n", n); |
090089c4 | 1658 | count += proc(e, data); |
1659 | } | |
1660 | return count; | |
1661 | } | |
1662 | ||
1663 | ||
1664 | /* compare an object timestamp and see if ttl is expired. Free it if so. */ | |
1665 | /* return 1 if it expired, 0 if not */ | |
1666 | int removeOldEntry(e, data) | |
1667 | StoreEntry *e; | |
2318883b | 1668 | void *data; |
090089c4 | 1669 | { |
1670 | time_t curtime = *((time_t *) data); | |
1671 | ||
c943f331 | 1672 | debug(20, 5, "removeOldEntry: Checking: %s\n", e->url); |
1673 | debug(20, 6, "removeOldEntry: * curtime: %8ld\n", curtime); | |
1674 | debug(20, 6, "removeOldEntry: * e->timestamp: %8ld\n", e->timestamp); | |
1675 | debug(20, 6, "removeOldEntry: * time in cache: %8ld\n", | |
090089c4 | 1676 | curtime - e->timestamp); |
c943f331 | 1677 | debug(20, 6, "removeOldEntry: * time-to-live: %8ld\n", |
090089c4 | 1678 | e->expires - cached_curtime); |
1679 | ||
1680 | if ((cached_curtime > e->expires) && (e->status != STORE_PENDING)) { | |
1681 | return (storeRelease(e) == 0 ? 1 : 0); | |
1682 | } | |
1683 | return 0; | |
1684 | } | |
1685 | ||
1686 | ||
1687 | /* free up all ttl-expired objects */ | |
1688 | int storePurgeOld() | |
1689 | { | |
1690 | int n; | |
1691 | ||
c943f331 | 1692 | debug(20, 3, "storePurgeOld: Begin purging TTL-expired objects\n"); |
2318883b | 1693 | n = storeWalkThrough(removeOldEntry, (void *) &cached_curtime); |
c943f331 | 1694 | debug(20, 3, "storePurgeOld: Done purging TTL-expired objects.\n"); |
1695 | debug(20, 3, "storePurgeOld: %d objects expired\n", n); | |
090089c4 | 1696 | return n; |
1697 | } | |
1698 | ||
1699 | ||
1700 | #define MEM_LRUSCAN_BLOCK 16 | |
1701 | #define MEM_MAX_HELP 5 | |
1702 | /* Clear Memory storage to accommodate the given object len */ | |
1703 | int storeGetMemSpace(size, check_vm_number) | |
1704 | int size; | |
1705 | int check_vm_number; | |
1706 | { | |
1707 | static int over_highwater = 0; | |
1708 | static int over_max = 0; | |
1709 | StoreEntry *LRU = NULL, *e = NULL; | |
1710 | dynamic_array *LRU_list = NULL; | |
1711 | dynamic_array *pending_entry_list = NULL; | |
1712 | int entry_to_delete_behind = 0; | |
1713 | int n_deleted_behind = 0; | |
1714 | int n_scanned = 0; | |
1715 | int n_expired = 0; | |
1716 | int n_aborted = 0; | |
1717 | int n_purged = 0; | |
1718 | int n_released = 0; | |
1719 | int i; | |
1720 | int n_inmem = 0; /* extra debugging */ | |
1721 | int n_cantpurge = 0; /* extra debugging */ | |
1722 | int mem_cantpurge = 0; /* extra debugging */ | |
1723 | int compareLastRef(); | |
1724 | int compareSize(); | |
1725 | ||
1726 | ||
1727 | if (!check_vm_number && ((store_mem_size + size) < store_mem_high)) | |
1728 | return 0; | |
1729 | ||
c943f331 | 1730 | debug(20, 2, "storeGetMemSpace: Starting...\n"); |
090089c4 | 1731 | |
1732 | LRU_list = create_dynamic_array(meta_data.store_in_mem_objects, MEM_LRUSCAN_BLOCK); | |
1733 | pending_entry_list = create_dynamic_array(meta_data.store_in_mem_objects, MEM_LRUSCAN_BLOCK); | |
1734 | ||
1735 | for (e = storeGetInMemFirst(); e; e = storeGetInMemNext()) { | |
1736 | n_scanned++; | |
1737 | ||
1738 | n_inmem++; | |
1739 | ||
1740 | if (e->status == STORE_PENDING) { | |
1741 | if (!(e->flag & DELETE_BEHIND)) { | |
1742 | /* it's not deleting behind, we can do something about it. */ | |
1743 | insert_dynamic_array(pending_entry_list, e); | |
1744 | } | |
1745 | continue; | |
1746 | } | |
1747 | if (cached_curtime > e->expires) { | |
c943f331 | 1748 | debug(20, 2, "storeGetMemSpace: Expired: %s\n", e->url); |
090089c4 | 1749 | n_expired++; |
1750 | /* Delayed release */ | |
1751 | storeRelease(e); | |
1752 | continue; | |
1753 | } | |
1754 | if ((e->swap_status == SWAP_OK) && (e->mem_status != SWAPPING_IN) && | |
1755 | (e->lock_count == 0)) { | |
1756 | insert_dynamic_array(LRU_list, e); | |
1757 | } else if (((e->status == STORE_ABORTED) || | |
1758 | (e->swap_status == NO_SWAP)) && | |
1759 | (e->lock_count == 0)) { | |
1760 | n_aborted++; | |
1761 | insert_dynamic_array(LRU_list, e); | |
1762 | } else { | |
1763 | n_cantpurge++; | |
22e4fa85 | 1764 | mem_cantpurge += e->mem_obj->e_current_len; |
c943f331 | 1765 | debug(20, 5, "storeGetMemSpace: Can't purge %7d bytes: %s\n", |
22e4fa85 | 1766 | e->mem_obj->e_current_len, e->url); |
090089c4 | 1767 | if (e->swap_status != SWAP_OK) |
c943f331 | 1768 | debug(20, 5, "storeGetMemSpace: --> e->swap_status != SWAP_OK\n"); |
090089c4 | 1769 | if (e->lock_count) |
c943f331 | 1770 | debug(20, 5, "storeGetMemSpace: --> e->lock_count %d\n", e->lock_count); |
090089c4 | 1771 | } |
1772 | } | |
6602e70e | 1773 | debug(20, 5, "storeGetMemSpace: Current size: %7d bytes\n", store_mem_size); |
1774 | debug(20, 5, "storeGetMemSpace: High W Mark: %7d bytes\n", store_mem_high); | |
1775 | debug(20, 5, "storeGetMemSpace: Low W Mark: %7d bytes\n", store_mem_low); | |
1776 | debug(20, 5, "storeGetMemSpace: Entry count: %7d items\n", meta_data.store_entries); | |
1777 | debug(20, 5, "storeGetMemSpace: Scanned: %7d items\n", n_scanned); | |
1778 | debug(20, 5, "storeGetMemSpace: In memory: %7d items\n", n_inmem); | |
1779 | debug(20, 5, "storeGetMemSpace: Hot vm count: %7d items\n", meta_data.hot_vm); | |
1780 | debug(20, 5, "storeGetMemSpace: Expired: %7d items\n", n_expired); | |
1781 | debug(20, 5, "storeGetMemSpace: Negative Cached: %7d items\n", n_aborted); | |
1782 | debug(20, 5, "storeGetMemSpace: Can't purge: %7d items\n", n_cantpurge); | |
1783 | debug(20, 5, "storeGetMemSpace: Can't purge size: %7d bytes\n", mem_cantpurge); | |
1784 | debug(20, 5, "storeGetMemSpace: Sorting LRU_list: %7d items\n", LRU_list->index); | |
090089c4 | 1785 | qsort((char *) LRU_list->collection, LRU_list->index, sizeof(e), (int (*)(const void *, const void *)) compareLastRef); |
1786 | ||
1787 | /* Kick LRU out until we have enough memory space */ | |
1788 | ||
1789 | if (check_vm_number) { | |
1790 | /* look for vm slot */ | |
1791 | for (i = 0; (i < LRU_list->index) && (meta_data.hot_vm > store_hotobj_low); ++i) { | |
1792 | if ((LRU = (StoreEntry *) LRU_list->collection[i])) | |
1793 | if ((LRU->status != STORE_PENDING) && (LRU->swap_status == NO_SWAP)) { | |
1794 | n_released++; | |
1795 | storeRelease(LRU); | |
1796 | } else { | |
1797 | n_purged++; | |
1798 | storePurgeMem(LRU); | |
1799 | } | |
1800 | } | |
1801 | } else { | |
1802 | /* look for space */ | |
1803 | for (i = 0; (i < LRU_list->index) && ((store_mem_size + size) > store_mem_low); ++i) { | |
1804 | if ((LRU = (StoreEntry *) LRU_list->collection[i])) | |
1805 | if ((LRU->status != STORE_PENDING) && (LRU->swap_status == NO_SWAP)) { | |
1806 | n_released++; | |
1807 | storeRelease(LRU); | |
1808 | } else { | |
1809 | n_purged++; | |
1810 | storePurgeMem(LRU); | |
1811 | } | |
1812 | } | |
1813 | } | |
1814 | ||
1815 | destroy_dynamic_array(LRU_list); | |
1816 | ||
c943f331 | 1817 | debug(20, 2, "storeGetMemSpace: After freeing size: %7d bytes\n", store_mem_size); |
1818 | debug(20, 2, "storeGetMemSpace: Purged: %7d items\n", n_purged); | |
1819 | debug(20, 2, "storeGetMemSpace: Released: %7d items\n", n_released); | |
090089c4 | 1820 | |
1821 | ||
1822 | if (check_vm_number) { | |
1823 | /* don't check for size */ | |
1824 | destroy_dynamic_array(pending_entry_list); | |
c943f331 | 1825 | debug(20, 2, "storeGetMemSpace: Done.\n"); |
090089c4 | 1826 | return 0; |
1827 | } | |
1828 | if ((store_mem_size + size) < store_mem_high) { | |
1829 | /* we don't care for hot_vm count here, just the storage size. */ | |
1830 | over_highwater = over_max = 0; | |
1831 | destroy_dynamic_array(pending_entry_list); | |
c943f331 | 1832 | debug(20, 2, "storeGetMemSpace: Done.\n"); |
090089c4 | 1833 | return 0; |
1834 | } | |
1835 | if ((store_mem_size + size) < getCacheMemMax()) { | |
1836 | /* We're over high water mark here, but still under absolute max */ | |
1837 | if (!over_highwater) { | |
1838 | /* print only once when the condition occur until it clears. */ | |
c943f331 | 1839 | debug(20, 1, "storeGetMemSpace: Allocating beyond the high water mark with total size of %d\n", |
090089c4 | 1840 | store_mem_size + size); |
1841 | over_highwater = 1; | |
1842 | } | |
1843 | /* we can delete more than one if we want to be more aggressive. */ | |
1844 | entry_to_delete_behind = 1; | |
1845 | } else { | |
1846 | /* We're over absolute max */ | |
1847 | if (!over_max) { | |
1848 | /* print only once when the condition occur until it clears. */ | |
c943f331 | 1849 | debug(20, 1, "storeGetMemSpace: Allocating beyond the MAX Store with total size of %d\n", |
090089c4 | 1850 | store_mem_size + size); |
c943f331 | 1851 | debug(20, 1, " Start Deleting Behind for every pending objects\n:"); |
1852 | debug(20, 1, " You should really adjust your cache_mem, high/low water mark,\n"); | |
1853 | debug(20, 1, " max object size to suit your need.\n"); | |
090089c4 | 1854 | over_max = 1; |
1855 | } | |
1856 | /* delete all of them, we desperate for a space. */ | |
1857 | entry_to_delete_behind = pending_entry_list->index; | |
1858 | } | |
1859 | ||
1860 | /* sort the stuff by size */ | |
1861 | qsort((char *) pending_entry_list->collection, pending_entry_list->index, sizeof(e), (int (*)(const void *, const void *)) compareSize); | |
1862 | for (i = 0; (i < pending_entry_list->index) && (i < entry_to_delete_behind); ++i) | |
1863 | if (pending_entry_list->collection[i]) { | |
1864 | n_deleted_behind++; | |
1865 | storeStartDeleteBehind(pending_entry_list->collection[i]); | |
1866 | } | |
1867 | if (n_deleted_behind) { | |
c943f331 | 1868 | debug(20, 1, "storeGetMemSpace: Due to memory flucuation, put %d objects to DELETE_BEHIND MODE.\n", |
090089c4 | 1869 | n_deleted_behind); |
1870 | } | |
1871 | destroy_dynamic_array(pending_entry_list); | |
c943f331 | 1872 | debug(20, 2, "storeGetMemSpace: Done.\n"); |
090089c4 | 1873 | return 0; |
1874 | } | |
1875 | ||
1876 | int compareSize(e1, e2) | |
1877 | StoreEntry **e1, **e2; | |
1878 | { | |
1879 | if (!e1 || !e2) { | |
c943f331 | 1880 | debug(20, 1, "compareSize: Called with at least one null argument, shouldn't happen.\n"); |
090089c4 | 1881 | return 0; |
1882 | } | |
22e4fa85 | 1883 | if ((*e1)->mem_obj->e_current_len > (*e2)->mem_obj->e_current_len) |
090089c4 | 1884 | return (1); |
1885 | ||
22e4fa85 | 1886 | if ((*e1)->mem_obj->e_current_len < (*e2)->mem_obj->e_current_len) |
090089c4 | 1887 | return (-1); |
1888 | ||
1889 | return (0); | |
1890 | } | |
1891 | ||
1892 | int compareLastRef(e1, e2) | |
1893 | StoreEntry **e1, **e2; | |
1894 | { | |
1895 | if (!e1 || !e2) | |
1896 | fatal_dump(NULL); | |
1897 | ||
1898 | if ((*e1)->lastref > (*e2)->lastref) | |
1899 | return (1); | |
1900 | ||
1901 | if ((*e1)->lastref < (*e2)->lastref) | |
1902 | return (-1); | |
1903 | ||
1904 | return (0); | |
1905 | } | |
1906 | ||
1907 | /* returns the bucket number to work on, | |
1908 | * pointer to next bucket after each calling | |
1909 | */ | |
1910 | unsigned int storeGetBucketNum() | |
1911 | { | |
1912 | static unsigned int bucket = 0; | |
1913 | ||
1914 | if (bucket >= STORE_BUCKETS) | |
1915 | bucket = 0; | |
1916 | return (bucket++); | |
1917 | } | |
1918 | ||
1919 | #define SWAP_LRUSCAN_BLOCK 16 | |
1920 | #define SWAP_MAX_HELP STORE_BUCKETS/2 | |
1921 | ||
1922 | /* The maximum objects to scan for maintain storage space */ | |
1923 | #define SWAP_LRUSCAN_COUNT (256) | |
1924 | ||
1925 | /* Removes at most 30 LRU objects for one loop */ | |
1926 | #define SWAP_LRU_REMOVE_COUNT (8) | |
1927 | ||
1928 | /* Clear Swap storage to accommodate the given object len */ | |
1929 | int storeGetSwapSpace(size) | |
1930 | int size; | |
1931 | { | |
1932 | static int fReduceSwap = 0; | |
1933 | static int swap_help = 0; | |
1934 | StoreEntry *LRU = NULL, *e = NULL; | |
1935 | int scanned = 0; | |
1936 | int removed = 0; | |
1937 | int expired = 0; | |
1938 | int locked = 0; | |
1939 | int locked_size = 0; | |
1940 | int scan_in_objs = 0; | |
1941 | int i; | |
1942 | int LRU_cur_size = meta_data.store_entries; | |
1943 | dynamic_array *LRU_list; | |
1944 | hash_link *link_ptr = NULL, *next = NULL; | |
1945 | unsigned int kb_size = ((size + 1023) >> 10); | |
1946 | ||
1947 | if (store_swap_size + kb_size <= store_swap_low) | |
1948 | fReduceSwap = 0; | |
1949 | if (!fReduceSwap && (store_swap_size + kb_size <= store_swap_high)) { | |
1950 | return 0; | |
1951 | } | |
c943f331 | 1952 | debug(20, 2, "storeGetSwapSpace: Starting...\n"); |
090089c4 | 1953 | |
1954 | /* Set flag if swap size over high-water-mark */ | |
1955 | if (store_swap_size + kb_size > store_swap_high) | |
1956 | fReduceSwap = 1; | |
1957 | ||
c943f331 | 1958 | debug(20, 2, "storeGetSwapSpace: Need %d bytes...\n", size); |
090089c4 | 1959 | |
1960 | LRU_list = create_dynamic_array(LRU_cur_size, LRU_cur_size); | |
1961 | /* remove expired objects until recover enough space or no expired objects */ | |
1962 | for (i = 0; i < STORE_BUCKETS; ++i) { | |
1963 | int expired_in_one_bucket = 0; | |
1964 | ||
1965 | link_ptr = hash_get_bucket(table, storeGetBucketNum()); | |
1966 | if (link_ptr == NULL) | |
1967 | continue; | |
1968 | /* this while loop handles one bucket of hash table */ | |
1969 | expired_in_one_bucket = 0; | |
1970 | while (link_ptr) { | |
1971 | scanned++; | |
1972 | next = link_ptr->next; | |
1973 | e = (StoreEntry *) link_ptr; | |
1974 | ||
1975 | /* Identify objects that aren't locked, for replacement */ | |
1976 | if ((e->status != STORE_PENDING) && /* We're still fetching the object */ | |
1977 | (e->swap_status == SWAP_OK) && /* Only release it if it is on disk */ | |
1978 | (e->lock_count == 0) && /* Be overly cautious */ | |
1979 | (e->mem_status != SWAPPING_IN)) { /* Not if it's being faulted into memory */ | |
1980 | if (cached_curtime > e->expires) { | |
2285407f | 1981 | debug(20, 2, "storeGetSwapSpace: Expired: <URL:%s>\n", e->url); |
090089c4 | 1982 | /* just call release. don't have to check for lock status. |
1983 | * storeRelease will take care of that and set a pending flag | |
1984 | * if it's still locked. */ | |
1985 | ++expired_in_one_bucket; | |
1986 | storeRelease(e); | |
1987 | } else { | |
1988 | /* Prepare to do LRU replacement */ | |
1989 | insert_dynamic_array(LRU_list, e); | |
1990 | ++scan_in_objs; | |
1991 | } | |
1992 | } else { | |
c943f331 | 1993 | debug(20, 2, "storeGetSwapSpace: Can't purge %7d bytes: <URL:%s>\n", |
090089c4 | 1994 | e->object_len, e->url); |
1995 | if (e->lock_count) { | |
c943f331 | 1996 | debug(20, 2, "\t\te->lock_count %d\n", e->lock_count); |
090089c4 | 1997 | } |
1998 | if (e->swap_status == SWAPPING_OUT) { | |
c943f331 | 1999 | debug(20, 2, "\t\te->swap_status == SWAPPING_OUT\n"); |
090089c4 | 2000 | } |
2001 | locked++; | |
22e4fa85 | 2002 | locked_size += e->mem_obj->e_current_len; |
090089c4 | 2003 | } |
2004 | link_ptr = next; | |
2005 | } /* while, end of one bucket of hash table */ | |
2006 | ||
2007 | expired += expired_in_one_bucket; | |
2008 | if (expired_in_one_bucket && | |
2009 | ((!fReduceSwap && (store_swap_size + kb_size <= store_swap_high)) || | |
2010 | (fReduceSwap && (store_swap_size + kb_size <= store_swap_low))) | |
2011 | ) { | |
2012 | fReduceSwap = 0; | |
2013 | destroy_dynamic_array(LRU_list); | |
c943f331 | 2014 | debug(20, 2, "storeGetSwapSpace: Finished, %d objects expired.\n", |
090089c4 | 2015 | expired); |
2016 | return 0; | |
2017 | } | |
2018 | qsort((char *) LRU_list->collection, LRU_list->index, sizeof(e), (int (*)(const void *, const void *)) compareLastRef); | |
2019 | /* keep the first n LRU objects only */ | |
2020 | cut_dynamic_array(LRU_list, SWAP_LRU_REMOVE_COUNT); | |
2021 | ||
2022 | /* Scan in about SWAP_LRU_COUNT for one call */ | |
2023 | if (scan_in_objs >= SWAP_LRUSCAN_COUNT) | |
2024 | break; | |
2025 | } /* for */ | |
2026 | ||
2027 | /* end of candidate selection */ | |
c943f331 | 2028 | debug(20, 2, "storeGetSwapSpace: Current Size: %7d kbytes\n", store_swap_size); |
2029 | debug(20, 2, "storeGetSwapSpace: High W Mark: %7d kbytes\n", store_swap_high); | |
2030 | debug(20, 2, "storeGetSwapSpace: Low W Mark: %7d kbytes\n", store_swap_low); | |
2031 | debug(20, 2, "storeGetSwapSpace: Entry count: %7d items\n", meta_data.store_entries); | |
2032 | debug(20, 2, "storeGetSwapSpace: Scanned: %7d items\n", scanned); | |
2033 | debug(20, 2, "storeGetSwapSpace: Expired: %7d items\n", expired); | |
2034 | debug(20, 2, "storeGetSwapSpace: Locked: %7d items\n", locked); | |
2035 | debug(20, 2, "storeGetSwapSpace: Locked Space: %7d bytes\n", locked_size); | |
2036 | debug(20, 2, "storeGetSwapSpace: Scan in array: %7d bytes\n", scan_in_objs); | |
2037 | debug(20, 2, "storeGetSwapSpace: LRU candidate: %7d items\n", LRU_list->index); | |
090089c4 | 2038 | |
2039 | /* Although all expired objects removed, still didn't recover enough */ | |
2040 | /* space. Kick LRU out until we have enough swap space */ | |
2041 | for (i = 0; i < LRU_list->index; ++i) { | |
2042 | if (store_swap_size + kb_size <= store_swap_low) { | |
2043 | fReduceSwap = 0; | |
2044 | break; | |
2045 | } | |
2046 | if ((LRU = LRU_list->collection[i]) != NULL) { | |
2047 | if (storeRelease(LRU) == 0) { | |
2048 | removed++; | |
2049 | } else { | |
c943f331 | 2050 | debug(20, 2, "storeGetSwapSpace: Help! Can't remove objects. <%s>\n", |
090089c4 | 2051 | LRU->url); |
2052 | } | |
2053 | } | |
2054 | } | |
c943f331 | 2055 | debug(20, 2, "storeGetSwapSpace: After Freeing Size: %7d kbytes\n", store_swap_size); |
090089c4 | 2056 | |
2057 | /* free the list */ | |
2058 | destroy_dynamic_array(LRU_list); | |
2059 | ||
2060 | if ((store_swap_size + kb_size > store_swap_high)) { | |
2061 | if (++swap_help > SWAP_MAX_HELP) { | |
c943f331 | 2062 | debug(20, 0, "storeGetSwapSpace: Nothing to free with %d Kbytes in use.\n", |
090089c4 | 2063 | store_swap_size); |
c943f331 | 2064 | debug(20, 0, "--> Asking for %d bytes\n", size); |
2065 | debug(20, 0, "WARNING! Repeated failures to allocate swap space!\n"); | |
2066 | debug(20, 0, "WARNING! Please check your disk space.\n"); | |
090089c4 | 2067 | swap_help = 0; |
2068 | } else { | |
c943f331 | 2069 | debug(20, 2, "storeGetSwapSpace: Nothing to free with %d Kbytes in use.\n", |
090089c4 | 2070 | store_swap_size); |
c943f331 | 2071 | debug(20, 2, "--> Asking for %d bytes\n", size); |
090089c4 | 2072 | } |
2073 | } else { | |
2074 | swap_help = 0; | |
2075 | } | |
2076 | ||
c943f331 | 2077 | debug(20, 2, "storeGetSwapSpace: Finished, %d objects removed.\n", removed); |
090089c4 | 2078 | return 0; |
2079 | } | |
2080 | ||
2081 | ||
2082 | /* release an object from a cache */ | |
2083 | /* return 0 when success. */ | |
2084 | int storeRelease(e) | |
2085 | StoreEntry *e; | |
2086 | { | |
2087 | StoreEntry *result = NULL; | |
2088 | StoreEntry *head_result = NULL; | |
2089 | hash_link *hptr = NULL; | |
227fbb74 | 2090 | hash_link *head_table_entry = NULL; |
090089c4 | 2091 | |
6602e70e | 2092 | debug(20, 3, "storeRelease: Releasing: '%s'\n", e->key); |
2093 | ||
090089c4 | 2094 | /* If, for any reason we can't discard this object because of an |
2095 | * outstanding request, mark it for pending release */ | |
2096 | if (storeEntryLocked(e)) { | |
9174e204 | 2097 | storeExpireNow(e); |
6602e70e | 2098 | debug(20, 3, "storeRelease: Only setting RELEASE_REQUEST bit\n"); |
2daae136 | 2099 | storeReleaseRequest(e); |
090089c4 | 2100 | return -1; |
2101 | } | |
227fbb74 | 2102 | if (e->key != NULL) { |
2103 | if ((hptr = hash_lookup(table, e->key)) == NULL) { | |
2285407f | 2104 | debug(20, 0, "storeRelease: Not Found: '%s'\n", e->key); |
227fbb74 | 2105 | debug(20, 0, "Dump of Entry 'e':\n %s\n", storeToString(e)); |
2106 | fatal_dump(NULL); | |
2107 | } | |
2108 | result = (StoreEntry *) hptr; | |
2109 | if (result != e) { | |
2110 | debug(20, 0, "storeRelease: Duplicated entry? <URL:%s>\n", | |
2111 | result->url ? result->url : "NULL"); | |
2112 | debug(20, 0, "Dump of Entry 'e':\n%s", storeToString(e)); | |
2113 | debug(20, 0, "Dump of Entry 'result':\n%s", storeToString(result)); | |
2114 | fatal_dump(NULL); | |
2115 | } | |
090089c4 | 2116 | } |
73ddce89 | 2117 | if (e->method == METHOD_GET) { |
090089c4 | 2118 | /* check if coresponding HEAD object exists. */ |
227fbb74 | 2119 | head_table_entry = hash_lookup(table, |
6eb42cae | 2120 | storeGeneratePublicKey(e->url, METHOD_HEAD)); |
090089c4 | 2121 | if (head_table_entry) { |
2122 | head_result = (StoreEntry *) head_table_entry; | |
2123 | if (head_result) { | |
2124 | /* recursive call here to free up /head/ */ | |
2125 | storeRelease(head_result); | |
2126 | } | |
2127 | } | |
2128 | } | |
227fbb74 | 2129 | if (e->key) |
2130 | debug(20, 5, "storeRelease: Release object key: %s\n", e->key); | |
2131 | else | |
2132 | debug(20, 5, "storeRelease: Release anonymous object\n"); | |
090089c4 | 2133 | |
2134 | if (e->swap_status == SWAP_OK && (e->swap_file_number > -1)) { | |
2135 | (void) safeunlink(storeSwapFullPath(e->swap_file_number, NULL), 0); | |
2136 | file_map_bit_reset(e->swap_file_number); | |
2137 | e->swap_file_number = -1; | |
2138 | store_swap_size -= (e->object_len + 1023) >> 10; | |
2139 | } | |
2140 | /* Discard byte count */ | |
2141 | CacheInfo->proto_purgeobject(CacheInfo, | |
2142 | CacheInfo->proto_id(e->url), | |
2143 | e->object_len); | |
227fbb74 | 2144 | if (hptr) |
2145 | storeHashDelete(hptr); | |
147d3115 | 2146 | storeLog(STORE_LOG_RELEASE, e); |
090089c4 | 2147 | storeFreeEntry(e); |
2148 | return 0; | |
2149 | } | |
2150 | ||
2151 | ||
090089c4 | 2152 | /* return if the current key is the original one. */ |
2153 | int storeOriginalKey(e) | |
2154 | StoreEntry *e; | |
2155 | { | |
2156 | if (!e) | |
2157 | return 1; | |
090089c4 | 2158 | return !(e->flag & KEY_CHANGE); |
2159 | } | |
2160 | ||
2161 | /* return 1 if a store entry is locked */ | |
2162 | int storeEntryLocked(e) | |
2163 | StoreEntry *e; | |
2164 | { | |
2165 | if (!e) { | |
c943f331 | 2166 | debug(20, 0, "This entry should be valid.\n"); |
2167 | debug(20, 0, "%s", storeToString(e)); | |
090089c4 | 2168 | fatal_dump(NULL); |
2169 | } | |
2170 | return ((e->lock_count) || | |
2171 | (e->status == STORE_PENDING) || | |
2172 | (e->swap_status == SWAPPING_OUT) || | |
2173 | (e->mem_status == SWAPPING_IN) | |
2174 | ); | |
2175 | } | |
2176 | ||
2177 | /* use this for internal call only */ | |
2178 | int storeCopy(e, stateoffset, maxSize, buf, size) | |
2179 | StoreEntry *e; | |
2180 | int stateoffset; | |
2181 | int maxSize; | |
2182 | char *buf; | |
2183 | int *size; | |
2184 | { | |
2185 | int available_to_write = 0; | |
2186 | ||
22e4fa85 | 2187 | available_to_write = e->mem_obj->e_current_len - stateoffset; |
090089c4 | 2188 | |
22e4fa85 | 2189 | if (stateoffset < e->mem_obj->e_lowest_offset) { |
090089c4 | 2190 | /* this should not happen. Logic race !!! */ |
c943f331 | 2191 | debug(20, 1, "storeCopy: Client Request a chunk of data in area lower than the lowest_offset\n"); |
2192 | debug(20, 1, " Current Lowest offset : %d\n", e->mem_obj->e_lowest_offset); | |
2193 | debug(20, 1, " Requested offset : %d\n", stateoffset); | |
090089c4 | 2194 | /* can't really do anything here. Client may hang until lifetime runout. */ |
2195 | return 0; | |
2196 | } | |
2197 | *size = (available_to_write >= maxSize) ? | |
2198 | maxSize : available_to_write; | |
2199 | ||
c943f331 | 2200 | debug(20, 6, "storeCopy: avail_to_write=%d, store_offset=%d\n", |
090089c4 | 2201 | *size, stateoffset); |
2202 | ||
2203 | if (*size > 0) | |
22e4fa85 | 2204 | (void) e->mem_obj->data->mem_copy(e->mem_obj->data, stateoffset, buf, *size); |
090089c4 | 2205 | |
2206 | return *size; | |
2207 | } | |
2208 | ||
2209 | /* check if there is any client waiting for this object at all */ | |
2210 | /* return 1 if there is at least one client */ | |
2211 | int storeClientWaiting(e) | |
2212 | StoreEntry *e; | |
2213 | { | |
2214 | int i; | |
2215 | ||
22e4fa85 | 2216 | if (e->mem_obj->client_list) { |
2217 | for (i = 0; i < e->mem_obj->client_list_size; ++i) { | |
2218 | if (e->mem_obj->client_list[i]) | |
090089c4 | 2219 | return 1; |
2220 | } | |
2221 | } | |
22e4fa85 | 2222 | if (e->mem_obj->pending) { |
2223 | for (i = 0; i < (int) e->mem_obj->pending_list_size; ++i) { | |
2224 | if (e->mem_obj->pending[i]) | |
090089c4 | 2225 | return 1; |
2226 | } | |
2227 | } | |
2228 | return 0; | |
2229 | } | |
2230 | ||
2231 | /* return index to matched clientstatus in client_list, -1 on NOT_FOUND */ | |
2232 | int storeClientListSearch(e, fd) | |
2233 | StoreEntry *e; | |
2234 | int fd; | |
2235 | { | |
2236 | int i; | |
2237 | ||
22e4fa85 | 2238 | if (!e->mem_obj->client_list) |
090089c4 | 2239 | return -1; |
22e4fa85 | 2240 | for (i = 0; i < e->mem_obj->client_list_size; ++i) { |
2241 | if (e->mem_obj->client_list[i] && | |
2242 | (fd == e->mem_obj->client_list[i]->fd)) | |
090089c4 | 2243 | return i; |
2244 | } | |
2245 | return -1; | |
2246 | } | |
2247 | ||
2248 | /* add client with fd to client list */ | |
2249 | void storeClientListAdd(e, fd, last_offset) | |
2250 | StoreEntry *e; | |
2251 | int fd; | |
2252 | int last_offset; | |
2253 | { | |
2254 | int i; | |
2255 | /* look for empty slot */ | |
2256 | ||
22e4fa85 | 2257 | if (e->mem_obj->client_list) { |
2258 | for (i = 0; (i < e->mem_obj->client_list_size) | |
2259 | && (e->mem_obj->client_list[i] != NULL); ++i); | |
090089c4 | 2260 | |
22e4fa85 | 2261 | if (i == e->mem_obj->client_list_size) { |
090089c4 | 2262 | /* have to expand client_list */ |
22e4fa85 | 2263 | e->mem_obj->client_list_size += MIN_CLIENT; |
2264 | e->mem_obj->client_list = (ClientStatusEntry **) xrealloc(e->mem_obj->client_list, e->mem_obj->client_list_size * sizeof(ClientStatusEntry *)); | |
090089c4 | 2265 | } |
2266 | } else { | |
22e4fa85 | 2267 | e->mem_obj->client_list_size += MIN_CLIENT; |
2268 | e->mem_obj->client_list = (ClientStatusEntry **) xcalloc(e->mem_obj->client_list_size, sizeof(ClientStatusEntry *)); | |
090089c4 | 2269 | i = 0; |
2270 | } | |
2271 | ||
22e4fa85 | 2272 | e->mem_obj->client_list[i] = (ClientStatusEntry *) xcalloc(1, sizeof(ClientStatusEntry)); |
2273 | e->mem_obj->client_list[i]->fd = fd; | |
2274 | e->mem_obj->client_list[i]->last_offset = last_offset; | |
090089c4 | 2275 | } |
2276 | ||
2277 | /* same to storeCopy but also register client fd and last requested offset | |
2278 | * for each client */ | |
2279 | int storeClientCopy(e, stateoffset, maxSize, buf, size, fd) | |
2280 | StoreEntry *e; | |
2281 | int stateoffset; | |
2282 | int maxSize; | |
2283 | char *buf; | |
2284 | int *size; | |
2285 | int fd; | |
2286 | { | |
2287 | int next_offset; | |
2288 | int client_list_index; | |
22e4fa85 | 2289 | int available_to_write = e->mem_obj->e_current_len - stateoffset; |
090089c4 | 2290 | |
22e4fa85 | 2291 | if (stateoffset < e->mem_obj->e_lowest_offset) { |
090089c4 | 2292 | /* this should not happen. Logic race !!! */ |
c943f331 | 2293 | debug(20, 1, "storeClientCopy: Client Request a chunk of data in area lower than the lowest_offset\n"); |
2294 | debug(20, 1, " fd : %d\n", fd); | |
2295 | debug(20, 1, " Current Lowest offset : %d\n", e->mem_obj->e_lowest_offset); | |
2296 | debug(20, 1, " Requested offset : %d\n", stateoffset); | |
090089c4 | 2297 | /* can't really do anything here. Client may hang until lifetime runout. */ |
2298 | return 0; | |
2299 | } | |
2300 | *size = (available_to_write >= maxSize) ? | |
2301 | maxSize : available_to_write; | |
2302 | ||
c943f331 | 2303 | debug(20, 6, "storeCopy: avail_to_write=%d, store_offset=%d\n", |
090089c4 | 2304 | *size, stateoffset); |
2305 | ||
2306 | /* update the lowest requested offset */ | |
2307 | next_offset = (stateoffset + *size); | |
2308 | if ((client_list_index = storeClientListSearch(e, fd)) >= 0) { | |
22e4fa85 | 2309 | e->mem_obj->client_list[client_list_index]->last_offset = next_offset; |
090089c4 | 2310 | } else { |
2311 | storeClientListAdd(e, fd, next_offset); | |
2312 | } | |
2313 | ||
2314 | if (*size > 0) | |
22e4fa85 | 2315 | (void) e->mem_obj->data->mem_copy(e->mem_obj->data, stateoffset, buf, *size); |
090089c4 | 2316 | |
2317 | /* see if we can get rid of some data if we are in "delete behind" mode . */ | |
2318 | if (e->flag & DELETE_BEHIND) { | |
2319 | /* call the handler to delete behind the lowest offset */ | |
2320 | storeDeleteBehind(e); | |
2321 | } | |
2322 | return *size; | |
2323 | } | |
2324 | ||
090089c4 | 2325 | |
2326 | int storeEntryValidToSend(e) | |
2327 | StoreEntry *e; | |
2328 | { | |
9174e204 | 2329 | if (cached_curtime < e->expires) |
2330 | return 1; | |
2331 | if (e->expires == 0 && e->status == STORE_PENDING) | |
090089c4 | 2332 | return 1; |
2333 | return 0; | |
2334 | } | |
2335 | ||
6602e70e | 2336 | int storeEntryValidLength(e) |
2337 | StoreEntry *e; | |
2338 | { | |
ffe4a367 | 2339 | int diff; |
2285407f | 2340 | int hdr_sz; |
2341 | int content_length; | |
ffe4a367 | 2342 | |
2285407f | 2343 | if (e->mem_obj == NULL) |
ffe4a367 | 2344 | fatal_dump("storeEntryValidLength: NULL mem_obj"); |
2345 | ||
2285407f | 2346 | hdr_sz = e->mem_obj->reply->hdr_sz; |
147d3115 | 2347 | content_length = e->mem_obj->reply->content_length; |
2285407f | 2348 | |
ffe4a367 | 2349 | debug(20, 3, "storeEntryValidLength: Checking '%s'\n", e->key); |
60bf30cb | 2350 | debug(20, 5, "storeEntryValidLength: object_len = %d\n", e->object_len); |
2285407f | 2351 | debug(20, 5, "storeEntryValidLength: hdr_sz = %d\n", hdr_sz); |
2352 | debug(20, 5, "storeEntryValidLength: content_length = %d\n", content_length); | |
ffe4a367 | 2353 | |
2285407f | 2354 | if (content_length == 0) { |
60bf30cb | 2355 | debug(20, 5, "storeEntryValidLength: Zero content length; assume valid; '%s'\n", |
ffe4a367 | 2356 | e->key); |
2357 | return 1; | |
2358 | } | |
2285407f | 2359 | if (hdr_sz == 0) { |
60bf30cb | 2360 | debug(20, 5, "storeEntryValidLength: Zero header size; assume valid; '%s'\n", |
ffe4a367 | 2361 | e->key); |
2362 | return 1; | |
2363 | } | |
2285407f | 2364 | diff = hdr_sz + content_length - e->object_len; |
ffe4a367 | 2365 | if (diff != 0) { |
60bf30cb | 2366 | debug(20, 3, "storeEntryValidLength: %d bytes too %s; '%s'\n", |
ffe4a367 | 2367 | diff < 0 ? -diff : diff, |
2368 | diff < 0 ? "small" : "big", | |
2369 | e->key); | |
2370 | return 0; | |
2371 | } | |
2372 | return 1; | |
2373 | } | |
6602e70e | 2374 | |
c943f331 | 2375 | static int storeVerifySwapDirs(clean) |
2376 | int clean; | |
090089c4 | 2377 | { |
090089c4 | 2378 | int inx; |
2379 | char *path = NULL; | |
c943f331 | 2380 | struct stat sb; |
2381 | int directory_created = 0; | |
2382 | char *cmdbuf = NULL; | |
090089c4 | 2383 | |
2384 | for (inx = 0; inx < ncache_dirs; ++inx) { | |
2385 | path = swappath(inx); | |
b37a37f9 | 2386 | debug(20, 10, "storeVerifySwapDirs: Creating swap space in %s\n", path); |
090089c4 | 2387 | if (stat(path, &sb) < 0) { |
2388 | /* we need to create a directory for swap file here. */ | |
2389 | if (mkdir(path, 0777) < 0) { | |
c943f331 | 2390 | if (errno != EEXIST) { |
2391 | sprintf(tmp_error_buf, "Failed to create swap directory %s: %s", | |
2392 | path, | |
2393 | xstrerror()); | |
2394 | fatal(tmp_error_buf); | |
2395 | } | |
090089c4 | 2396 | } |
2397 | if (stat(path, &sb) < 0) { | |
c943f331 | 2398 | sprintf(tmp_error_buf, |
2399 | "Failed to verify swap directory %s: %s", | |
090089c4 | 2400 | path, xstrerror()); |
c943f331 | 2401 | fatal(tmp_error_buf); |
090089c4 | 2402 | } |
b37a37f9 | 2403 | debug(20, 1, "storeVerifySwapDirs: Created swap directory %s\n", path); |
090089c4 | 2404 | directory_created = 1; |
2405 | } | |
c943f331 | 2406 | if (clean) { |
b37a37f9 | 2407 | debug(20, 1, "storeVerifySwapDirs: Zapping all objects on disk storage.\n"); |
c943f331 | 2408 | /* This could be dangerous, second copy of cache can destroy |
2409 | * the existing swap files of the previous cache. We may | |
2410 | * use rc file do it. */ | |
2411 | cmdbuf = xcalloc(1, BUFSIZ); | |
2412 | sprintf(cmdbuf, "cd %s; /bin/rm -rf log [0-9][0-9]", path); | |
b37a37f9 | 2413 | debug(20, 1, "storeVerifySwapDirs: Running '%s'\n", cmdbuf); |
c943f331 | 2414 | system(cmdbuf); /* XXX should avoid system(3) */ |
2415 | xfree(cmdbuf); | |
090089c4 | 2416 | } |
2417 | } | |
c943f331 | 2418 | return directory_created; |
2419 | } | |
090089c4 | 2420 | |
c943f331 | 2421 | static void storeCreateSwapSubDirs() |
2422 | { | |
2423 | int i, j; | |
2424 | static char name[1024]; | |
2425 | for (j = 0; j < ncache_dirs; j++) { | |
2426 | for (i = 0; i < SWAP_DIRECTORIES; i++) { | |
2427 | sprintf(name, "%s/%02d", swappath(j), i); | |
2428 | if (mkdir(name, 0755) < 0) { | |
2429 | if (errno != EEXIST) { | |
2430 | sprintf(tmp_error_buf, | |
2431 | "Failed to make swap directory %s: %s", | |
2432 | name, xstrerror()); | |
2433 | fatal(tmp_error_buf); | |
2434 | } | |
2435 | } | |
2436 | } | |
090089c4 | 2437 | } |
c943f331 | 2438 | } |
090089c4 | 2439 | |
c943f331 | 2440 | int storeInit() |
2441 | { | |
2442 | int dir_created; | |
0ffd22bc | 2443 | wordlist *w = NULL; |
c943f331 | 2444 | |
d8b45066 | 2445 | storelog_fd = file_open(getStoreLogFile(), NULL, O_WRONLY | O_APPEND | O_CREAT); |
2285407f | 2446 | |
d68adf86 | 2447 | for (w = getCacheDirs(); w; w = w->next) |
0ffd22bc | 2448 | storeAddSwapDisk(w->key); |
a26bdc75 | 2449 | storeSanityCheck(); |
c943f331 | 2450 | file_map_create(MAX_SWAP_FILE); |
2451 | dir_created = storeVerifySwapDirs(zap_disk_store); | |
b37a37f9 | 2452 | storeCreateHashTable(urlcmp); |
c943f331 | 2453 | |
2454 | sprintf(swaplog_file, "%s/log", swappath(0)); | |
090089c4 | 2455 | |
c943f331 | 2456 | swaplog_fd = file_open(swaplog_file, NULL, O_WRONLY | O_CREAT | O_APPEND); |
66a91d2d | 2457 | debug(20, 3, "swaplog_fd %d is now '%s'\n", swaplog_fd, swaplog_file); |
c943f331 | 2458 | if (swaplog_fd < 0) { |
2459 | sprintf(tmp_error_buf, "Cannot open swap logfile: %s", swaplog_file); | |
2460 | fatal(tmp_error_buf); | |
2461 | } | |
c943f331 | 2462 | swaplog_lock = file_write_lock(swaplog_fd); |
66a91d2d | 2463 | debug(20, 3, "swaplog_lock = %d\n", swaplog_lock); |
3facc31e | 2464 | |
2465 | if (!zap_disk_store) { | |
2466 | ok_write_clean_log = 0; | |
2467 | storeStartRebuildFromDisk(); | |
2468 | } else { | |
2469 | ok_write_clean_log = 1; | |
2470 | } | |
c943f331 | 2471 | |
2472 | if (dir_created || zap_disk_store) | |
2473 | storeCreateSwapSubDirs(); | |
2474 | ||
090089c4 | 2475 | store_mem_high = (long) (getCacheMemMax() / 100) * |
2476 | getCacheMemHighWaterMark(); | |
2477 | store_mem_low = (long) (getCacheMemMax() / 100) * | |
2478 | getCacheMemLowWaterMark(); | |
2479 | ||
2480 | store_hotobj_high = (int) (getCacheHotVmFactor() * | |
2481 | store_mem_high / (1 << 20)); | |
2482 | store_hotobj_low = (int) (getCacheHotVmFactor() * | |
2483 | store_mem_low / (1 << 20)); | |
2484 | ||
2485 | /* check for validity */ | |
2486 | if (store_hotobj_low > store_hotobj_high) | |
2487 | store_hotobj_low = store_hotobj_high; | |
2488 | ||
2489 | store_swap_high = (long) (getCacheSwapMax() / 100) * | |
2490 | getCacheSwapHighWaterMark(); | |
2491 | store_swap_low = (long) (getCacheSwapMax() / 100) * | |
2492 | getCacheSwapLowWaterMark(); | |
2493 | ||
2494 | return 0; | |
2495 | } | |
2496 | ||
2497 | /* | |
2498 | * storeSanityCheck - verify that all swap storage areas exist, and | |
2499 | * are writable; otherwise, force -z. | |
2500 | */ | |
2501 | void storeSanityCheck() | |
2502 | { | |
2503 | static char name[4096]; | |
090089c4 | 2504 | int i; |
2505 | ||
2506 | if (ncache_dirs < 1) | |
2507 | storeAddSwapDisk(DEFAULT_SWAP_DIR); | |
2508 | ||
2509 | for (i = 0; i < SWAP_DIRECTORIES; i++) { | |
2510 | sprintf(name, "%s/%02d", swappath(i), i); | |
2511 | errno = 0; | |
2512 | if (access(name, W_OK)) { | |
2513 | /* A very annoying problem occurs when access() fails because | |
2514 | * the system file table is full. To prevent cached from | |
2515 | * deleting your entire disk cache on a whim, insist that the | |
2516 | * errno indicates that the directory doesn't exist */ | |
2517 | if (errno != ENOENT) | |
2518 | continue; | |
cff333e8 | 2519 | debug(20, 0, "WARNING: Cannot write to '%s' for storage swap area.\n", name); |
a26bdc75 | 2520 | debug(20, 0, "Forcing a *full restart* (e.g., cached -z)...\n"); |
090089c4 | 2521 | zap_disk_store = 1; |
2522 | return; | |
2523 | } | |
2524 | } | |
2525 | } | |
2526 | ||
2527 | int urlcmp(url1, url2) | |
2528 | char *url1, *url2; | |
2529 | { | |
2530 | if (!url1 || !url2) | |
6eb42cae | 2531 | fatal_dump("urlcmp: Got a NULL url pointer."); |
090089c4 | 2532 | return (strcmp(url1, url2)); |
2533 | } | |
2534 | ||
2535 | int parse_file_number(s) | |
2536 | char *s; | |
2537 | { | |
2538 | int len; | |
2539 | ||
2540 | ||
2541 | for (len = strlen(s); (len >= 0); --len) { | |
2542 | if (s[len] == '/') { | |
2543 | return (atoi(&s[len + 1])); | |
2544 | } | |
2545 | } | |
c943f331 | 2546 | debug(20, 1, "parse_file_number: Could not determine the swap file number from %s.\n", s); |
090089c4 | 2547 | return (0); |
2548 | } | |
2549 | ||
2550 | /* | |
2551 | * This routine is to be called by main loop in main.c. | |
2552 | * It removes expired objects on only one bucket for each time called. | |
2553 | * returns the number of objects removed | |
2554 | * | |
2555 | * This should get called 1/s from main(). | |
2556 | */ | |
2557 | int storeMaintainSwapSpace() | |
2558 | { | |
2559 | static int loop_count = 0; | |
2560 | static unsigned int bucket = 0; | |
2561 | hash_link *link_ptr = NULL, *next = NULL; | |
2562 | StoreEntry *e = NULL; | |
2563 | int rm_obj = 0; | |
2564 | ||
090089c4 | 2565 | /* Scan row of hash table each second and free storage if we're |
2566 | * over the high-water mark */ | |
2567 | storeGetSwapSpace(0); | |
2568 | ||
2569 | /* Purges expired objects, check one bucket on each calling */ | |
2570 | if (loop_count++ >= STORE_MAINTAIN_RATE) { | |
2571 | loop_count = 0; | |
2572 | if (bucket >= STORE_BUCKETS) | |
2573 | bucket = 0; | |
2574 | link_ptr = hash_get_bucket(table, bucket++); | |
2575 | while (link_ptr) { | |
2576 | next = link_ptr->next; | |
2577 | e = (StoreEntry *) link_ptr; | |
2578 | if ((cached_curtime > e->expires) && | |
2579 | (e->swap_status == SWAP_OK)) { | |
c943f331 | 2580 | debug(20, 2, "storeMaintainSwapSpace: Expired: <TTL:%d> <URL:%s>\n", |
090089c4 | 2581 | e->expires - cached_curtime, e->url); |
2582 | /* just call release. don't have to check for lock status. | |
2583 | * storeRelease will take care of that and set a pending flag | |
2584 | * if it's still locked. */ | |
2585 | storeRelease(e); | |
2586 | ++rm_obj; | |
2587 | } | |
2588 | link_ptr = next; | |
2589 | } | |
2590 | } | |
2591 | return rm_obj; | |
2592 | } | |
2593 | ||
090089c4 | 2594 | |
2595 | /* | |
2596 | * storeWriteCleanLog | |
2597 | * | |
2598 | * Writes a "clean" swap log file from in-memory metadata. | |
2599 | */ | |
2600 | int storeWriteCleanLog() | |
2601 | { | |
2602 | StoreEntry *e = NULL; | |
2603 | static char swapfilename[MAX_FILE_NAME_LEN]; | |
090089c4 | 2604 | FILE *fp = NULL; |
2605 | int n = 0; | |
2606 | time_t start, stop, r; | |
2607 | ||
2608 | if (!ok_write_clean_log) { | |
c943f331 | 2609 | debug(20, 1, "storeWriteCleanLog: Not currently OK to rewrite swap log.\n"); |
2610 | debug(20, 1, "storeWriteCleanLog: Operation aborted.\n"); | |
090089c4 | 2611 | return 0; |
2612 | } | |
c943f331 | 2613 | debug(20, 1, "storeWriteCleanLog: Starting...\n"); |
b37a37f9 | 2614 | start = getCurrentTime(); |
3facc31e | 2615 | sprintf(tmp_filename, "%s/log_clean", swappath(0)); |
2616 | if ((fp = fopen(tmp_filename, "a+")) == NULL) { | |
2617 | debug(20, 0, "storeWriteCleanLog: %s: %s", tmp_filename, xstrerror()); | |
090089c4 | 2618 | return 0; |
2619 | } | |
2620 | for (e = storeGetFirst(); e; e = storeGetNext()) { | |
c943f331 | 2621 | debug(20, 5, "storeWriteCleanLog: <URL:%s>\n", e->url); |
090089c4 | 2622 | if (e->swap_file_number < 0) |
2623 | continue; | |
2624 | if (e->swap_status != SWAP_OK) | |
2625 | continue; | |
2626 | if (e->object_len <= 0) | |
2627 | continue; | |
2628 | storeSwapFullPath(e->swap_file_number, swapfilename); | |
c943f331 | 2629 | fprintf(fp, "%s %s %d %d %d\n", |
090089c4 | 2630 | swapfilename, e->url, (int) e->expires, (int) e->timestamp, |
2631 | e->object_len); | |
2632 | if ((++n & 0xFFF) == 0) { | |
b37a37f9 | 2633 | getCurrentTime(); |
c943f331 | 2634 | debug(20, 1, " %7d lines written so far.\n", n); |
090089c4 | 2635 | } |
2636 | } | |
2637 | fclose(fp); | |
2638 | ||
2639 | if (file_write_unlock(swaplog_fd, swaplog_lock) != DISK_OK) { | |
c943f331 | 2640 | debug(20, 0, "storeWriteCleanLog: Failed to unlock swaplog!\n"); |
2641 | debug(20, 0, "storeWriteCleanLog: Current swap logfile not replaced.\n"); | |
090089c4 | 2642 | return 0; |
2643 | } | |
3facc31e | 2644 | if (rename(tmp_filename, swaplog_file) < 0) { |
c943f331 | 2645 | debug(20, 0, "storeWriteCleanLog: rename failed: %s\n", |
090089c4 | 2646 | xstrerror()); |
2647 | return 0; | |
2648 | } | |
2649 | file_close(swaplog_fd); | |
b439a431 | 2650 | swaplog_fd = file_open(swaplog_file, NULL, O_RDWR | O_CREAT | O_APPEND); |
090089c4 | 2651 | if (swaplog_fd < 0) { |
c943f331 | 2652 | sprintf(tmp_error_buf, "Cannot open swap logfile: %s", swaplog_file); |
2653 | fatal(tmp_error_buf); | |
090089c4 | 2654 | } |
090089c4 | 2655 | swaplog_lock = file_write_lock(swaplog_fd); |
2656 | ||
b37a37f9 | 2657 | stop = getCurrentTime(); |
090089c4 | 2658 | r = stop - start; |
c943f331 | 2659 | debug(20, 1, " Finished. Wrote %d lines.\n", n); |
2660 | debug(20, 1, " Took %d seconds (%6.1lf lines/sec).\n", | |
090089c4 | 2661 | r > 0 ? r : 0, (double) n / (r > 0 ? r : 1)); |
2662 | ||
2663 | /* touch a timestamp file */ | |
3facc31e | 2664 | sprintf(tmp_filename, "%s/log-last-clean", swappath(0)); |
2665 | file_close(file_open(tmp_filename, NULL, O_WRONLY | O_CREAT | O_TRUNC)); | |
090089c4 | 2666 | return n; |
2667 | } | |
2668 | ||
2669 | int swapInError(fd_unused, entry) | |
2670 | int fd_unused; | |
2671 | StoreEntry *entry; | |
2672 | { | |
b367f261 | 2673 | cached_error_entry(entry, ERR_DISK_IO, xstrerror()); |
090089c4 | 2674 | return 0; |
2675 | } | |
2676 | ||
2677 | int storePendingNClients(e) | |
2678 | StoreEntry *e; | |
2679 | { | |
2680 | int npend = 0; | |
2681 | int i; | |
2682 | ||
2683 | if (!e->mem_obj) | |
2684 | return 0; | |
22e4fa85 | 2685 | for (npend = i = 0; i < (int) e->mem_obj->pending_list_size; i++) { |
2686 | if (e->mem_obj->pending[i]) | |
090089c4 | 2687 | npend++; |
2688 | } | |
2689 | return npend; | |
2690 | } | |
d8b45066 | 2691 | |
2692 | void storeRotateLog() | |
2693 | { | |
2694 | char *fname = NULL; | |
2695 | int i; | |
2696 | static char from[MAXPATHLEN]; | |
2697 | static char to[MAXPATHLEN]; | |
2698 | ||
2699 | if ((fname = getStoreLogFile()) == NULL) | |
2d25dc1a | 2700 | return; |
d8b45066 | 2701 | |
2702 | debug(20, 1, "storeRotateLog: Rotating.\n"); | |
2703 | ||
2704 | /* Rotate numbers 0 through N up one */ | |
2705 | for (i = getLogfileRotateNumber(); i > 1;) { | |
2d25dc1a | 2706 | i--; |
2707 | sprintf(from, "%s.%d", fname, i - 1); | |
2708 | sprintf(to, "%s.%d", fname, i); | |
2709 | rename(from, to); | |
d8b45066 | 2710 | } |
2711 | /* Rotate the current log to .0 */ | |
2712 | if (getLogfileRotateNumber() > 0) { | |
2d25dc1a | 2713 | sprintf(to, "%s.%d", fname, 0); |
2714 | rename(fname, to); | |
d8b45066 | 2715 | } |
2716 | if (storelog_fd > -1) | |
2d25dc1a | 2717 | file_close(storelog_fd); |
d8b45066 | 2718 | storelog_fd = file_open(getStoreLogFile(), NULL, O_WRONLY | O_APPEND | O_CREAT); |
2719 | } |