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