]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
Enhanced access logging. Now log HTTP reply code and elapsed
[thirdparty/squid.git] / src / store.cc
CommitLineData
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. */
55HashID table = 0;
56/* hash table for in-memory-only objects */
57HashID in_mem_table = 0;
58
59/* initializtion flag */
60static int ok_write_clean_log = 0;
61
62/* current memory storage size */
63static unsigned long store_mem_size = 0;
64static unsigned long store_mem_high = 0;
65static unsigned long store_mem_low = 0;
66
67/* current hotvm object */
68/* defaults for 16M cache and 12.5 cache_hot_vm_factor */
69static int store_hotobj_high = 180;
70static int store_hotobj_low = 120;
71
72
73/* current file name, swap file, use number as a filename */
74static unsigned long swapfileno = 0;
75static int store_swap_size = 0; /* kilobytes !! */
76static unsigned long store_swap_high = 0;
77static unsigned long store_swap_low = 0;
78static int swaplog_fd = 0;
79static int swaplog_lock;
090089c4 80FILE *swaplog_stream = NULL;
090089c4 81
82/* counter for uncachable objects */
090089c4 83static int keychange_count = 0;
84
85/* key temp buffer */
86static char key_temp_buffer[MAX_URL];
c943f331 87static char swaplog_file[MAX_FILE_NAME_LEN];
090089c4 88
89/* patch cache_dir to accomodate multiple disk storage */
90dynamic_array *cache_dirs = NULL;
91int ncache_dirs = 0;
92
227fbb74 93static 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
102static 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 115static 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 125static 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 137static 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 144static 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 */
162int 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
179HashID 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
191int 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
205int 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
224void 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 */
245void 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 */
283void 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 */
319int 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
358int 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. */
395StoreEntry *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
407char *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
431StoreEntry *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 488static 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
501void 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 521StoreEntry *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. */
619StoreEntry *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. */
671int 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 */
729int 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 */
765void 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. */
806static 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 */
830void 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 */
840void 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. */
858int 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 */
894int 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 */
904char *swappath(n)
905 int n;
906{
907 return cache_dirs->collection[n % ncache_dirs];
908}
909
910
911/* return full name to swapfile */
912char *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 */
934int 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 */
995int 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
1041void 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 */
1127int 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 */
1181void 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 */
1369int storeGetSwapSize()
1370{
1371 return store_swap_size;
1372}
1373
1374/* return current swap size in bytes */
1375int storeGetMemSize()
1376{
1377 return store_mem_size;
1378}
1379
1380
1381/* Complete transfer into the local cache. */
1382void 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 */
1417int 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 */
1474hash_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 */
1484hash_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 */
1493StoreEntry *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 */
1503StoreEntry *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 */
1511StoreEntry *storeGetFirst()
1512{
1513 return ((StoreEntry *) storeFindFirst(table));
1514}
1515
1516
1517/* get the next entry in the storage for a given search pointer */
1518StoreEntry *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 */
1526int 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 */
1547int 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 */
1569int 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 */
1584int 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
1757int 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
1773int 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 */
1791unsigned 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 */
1810int 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. */
1965int 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 */
2032void 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 2064void 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. */
2118int 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 */
2127int 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 */
2143int 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 */
2176int 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 */
2197int 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 */
2214void 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 */
2244int 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 */
2294char *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
2323int 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
2335int 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 2345static 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 2391static 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 2410int 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 */
2471void 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
2501int 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
2509int 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 */
2531int 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 */
2574int 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
2650int 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
2658int 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}