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