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