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