1 /*
2 MiddleMan filtering proxy server
3 Copyright (C) 2002 Jason McLaughlin
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <time.h>
27 #include <utime.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include "proto.h"
34
35 extern long gmtoffset;
36 extern GLOBAL *global;
37
38 /* hold lock when:
39 calling:
40 cache_hash_add
41 cache_hash_remove
42 cache_clean
43 cache_filename
44 cache_disk_delete
45 cache_mem_delete
46 cache_journal_*
47 *_unlocked
48 select_store
49
50 when accessing any members of a cachemap struct besides key, data, offset, and size.
51
52 no other threads may change any cachemap members when it's open for writing.
53
54 a thread with a cachemap open for writing may not change any member besides data without
55 the mutex locked.
56
57 cache->stores is protected with cache->lock
58 */
59
cache_load(struct cache_store * s,XML_LIST * xml_list)60 struct cache_store *cache_load(struct cache_store *s, XML_LIST * xml_list)
61 {
62 struct cache_store *head = s, *stores = s;
63
64 while ((xml_list = xml_section(xml_list, "<cache>"))) {
65 XML_LIST_LOOP(xml_list, "<cache>") {
66 XML_LIST_CMP(xml_list, "<enabled>") {
67 xml_list = xml_list->next;
68 if (xml_list->type == XML_VALUE) {
69 if (!strcasecmp(xml_list->item, "false"))
70 global->cache->enabled = FALSE;
71 else
72 global->cache->enabled = TRUE;
73 }
74 }
75 XML_LIST_CMP(xml_list, "<violaterfc>") {
76 xml_list = xml_list->next;
77 if (xml_list->type == XML_VALUE) {
78 if (!strcasecmp(xml_list->item, "false"))
79 global->cache->violaterfc = FALSE;
80 else
81 global->cache->violaterfc = TRUE;
82 }
83 }
84 XML_LIST_CMP(xml_list, "<maxmemsize>") {
85 xml_list = xml_list->next;
86 if (xml_list->type == XML_VALUE)
87 global_cache_insert(xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
88 }
89 XML_LIST_CMP(xml_list, "<memextra>") {
90 xml_list = xml_list->next;
91 if (xml_list->type == XML_VALUE)
92 global_cache_insert(NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
93 }
94 XML_LIST_CMP(xml_list, "<maxage>") {
95 xml_list = xml_list->next;
96 if (xml_list->type == XML_VALUE)
97 global_cache_insert(NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
98 }
99 XML_LIST_CMP(xml_list, "<validate>") {
100 xml_list = xml_list->next;
101 if (xml_list->type == XML_VALUE)
102 global_cache_insert(NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
103 }
104 XML_LIST_CMP(xml_list, "<lmfactor>") {
105 xml_list = xml_list->next;
106 if (xml_list->type == XML_VALUE)
107 global_cache_insert(NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
108 }
109 XML_LIST_CMP(xml_list, "<maxsize>") {
110 xml_list = xml_list->next;
111 if (xml_list->type == XML_VALUE)
112 global_cache_insert(NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
113 }
114 XML_LIST_CMP(xml_list, "<minage>") {
115 xml_list = xml_list->next;
116 if (xml_list->type == XML_VALUE)
117 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL);
118 }
119 XML_LIST_CMP(xml_list, "<minsize>") {
120 xml_list = xml_list->next;
121 if (xml_list->type == XML_VALUE)
122 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL);
123 }
124 XML_LIST_CMP(xml_list, "<maxwaitsize>") {
125 xml_list = xml_list->next;
126 if (xml_list->type == XML_VALUE)
127 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL);
128 }
129 XML_LIST_CMP(xml_list, "<prefetchwindow>") {
130 xml_list = xml_list->next;
131 if (xml_list->type == XML_VALUE)
132 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL);
133 }
134 XML_LIST_CMP(xml_list, "<icpport>") {
135 xml_list = xml_list->next;
136 if (xml_list->type == XML_VALUE)
137 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL);
138 }
139 XML_LIST_CMP(xml_list, "<icptimeout>") {
140 xml_list = xml_list->next;
141 if (xml_list->type == XML_VALUE)
142 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL);
143
144 }
145 XML_LIST_CMP(xml_list, "<sbalancemethod>") {
146 xml_list = xml_list->next;
147 if (xml_list->type == XML_VALUE)
148 global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item);
149
150 }
151
152 XML_LIST_CMP(xml_list, "<store>") {
153 stores = stores_list_new(stores);
154 if (head == NULL)
155 head = stores;
156
157 stores->id = global->cache->id++;
158
159 XML_LIST_LOOP(xml_list, "<store>") {
160 XML_LIST_CMP(xml_list, "<enabled>") {
161 xml_list = xml_list->next;
162 if (xml_list->type == XML_VALUE) {
163 if (!strcasecmp(xml_list->item, "false"))
164 stores->enabled = FALSE;
165 else
166 stores->enabled = TRUE;
167 }
168 }
169 XML_LIST_CMP(xml_list, "<comment>") {
170 xml_list = xml_list->next;
171 if (xml_list->type == XML_VALUE)
172 stores_list_insert(stores, xml_list->item, NULL, NULL, NULL, NULL);
173 }
174 XML_LIST_CMP(xml_list, "<profiles>") {
175 xml_list = xml_list->next;
176 if (xml_list->type == XML_VALUE)
177 stores_list_insert(stores, NULL, xml_list->item, NULL, NULL, NULL);
178 }
179 XML_LIST_CMP(xml_list, "<path>") {
180 xml_list = xml_list->next;
181 if (xml_list->type == XML_VALUE)
182 stores_list_insert(stores, NULL, NULL, xml_list->item, NULL, NULL);
183 }
184 XML_LIST_CMP(xml_list, "<maxdisksize>") {
185 xml_list = xml_list->next;
186 if (xml_list->type == XML_VALUE)
187 stores_list_insert(stores, NULL, NULL, NULL, xml_list->item, NULL);
188 }
189 XML_LIST_CMP(xml_list, "<diskextra>") {
190 xml_list = xml_list->next;
191 if (xml_list->type == XML_VALUE)
192 stores_list_insert(stores, NULL, NULL, NULL, NULL, xml_list->item);
193 }
194 }
195 }
196 }
197 }
198
199 return head;
200 }
201
cache_xml(struct cache_store * stores,XML_LIST * xml_list)202 XML_LIST *cache_xml(struct cache_store * stores, XML_LIST * xml_list)
203 {
204 char buf[16], *ptr;
205 struct cache_store *sl;
206
207 pthread_mutex_lock(&global->cache->lock);
208
209 xml_list = xml_list_add(xml_list, "<cache>", XML_TAG);
210
211 xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
212 xml_list = xml_list_add(xml_list, (global->cache->enabled == TRUE) ? "true" : "false", XML_VALUE);
213 xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
214
215 xml_list = xml_list_add(xml_list, "<violaterfc>", XML_TAG);
216 xml_list = xml_list_add(xml_list, (global->cache->violaterfc == FALSE) ? "false" : "true", XML_VALUE);
217 xml_list = xml_list_add(xml_list, "</violaterfc>", XML_TAG);
218
219 xml_list = xml_list_add(xml_list, "<maxmemsize>", XML_TAG);
220 snprintf(buf, sizeof(buf), "%u", global->cache->maxmemsize);
221 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
222 xml_list = xml_list_add(xml_list, "</maxmemsize>", XML_TAG);
223
224 xml_list = xml_list_add(xml_list, "<memextra>", XML_TAG);
225 snprintf(buf, sizeof(buf), "%u", global->cache->memextra);
226 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
227 xml_list = xml_list_add(xml_list, "</memextra>", XML_TAG);
228
229 xml_list = xml_list_add(xml_list, "<maxage>", XML_TAG);
230 snprintf(buf, sizeof(buf), "%d", global->cache->maxage);
231 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
232 xml_list = xml_list_add(xml_list, "</maxage>", XML_TAG);
233
234 xml_list = xml_list_add(xml_list, "<validate>", XML_TAG);
235 snprintf(buf, sizeof(buf), "%d", global->cache->validate);
236 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
237 xml_list = xml_list_add(xml_list, "</validate>", XML_TAG);
238
239 xml_list = xml_list_add(xml_list, "<lmfactor>", XML_TAG);
240 snprintf(buf, sizeof(buf), "%d", global->cache->lmfactor);
241 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
242 xml_list = xml_list_add(xml_list, "</lmfactor>", XML_TAG);
243
244 xml_list = xml_list_add(xml_list, "<maxsize>", XML_TAG);
245 snprintf(buf, sizeof(buf), "%u", global->cache->maxsize);
246 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
247 xml_list = xml_list_add(xml_list, "</maxsize>", XML_TAG);
248
249 xml_list = xml_list_add(xml_list, "<minage>", XML_TAG);
250 snprintf(buf, sizeof(buf), "%d", global->cache->minage);
251 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
252 xml_list = xml_list_add(xml_list, "</minage>", XML_TAG);
253
254 xml_list = xml_list_add(xml_list, "<minsize>", XML_TAG);
255 snprintf(buf, sizeof(buf), "%u", global->cache->minsize);
256 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
257 xml_list = xml_list_add(xml_list, "</minsize>", XML_TAG);
258
259 xml_list = xml_list_add(xml_list, "<maxwaitsize>", XML_TAG);
260 snprintf(buf, sizeof(buf), "%u", global->cache->maxwaitsize);
261 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
262 xml_list = xml_list_add(xml_list, "</maxwaitsize>", XML_TAG);
263
264 xml_list = xml_list_add(xml_list, "<prefetchwindow>", XML_TAG);
265 snprintf(buf, sizeof(buf), "%d", global->cache->prefetchwindow);
266 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
267 xml_list = xml_list_add(xml_list, "</prefetchwindow>", XML_TAG);
268
269 xml_list = xml_list_add(xml_list, "<icpport>", XML_TAG);
270 snprintf(buf, sizeof(buf), "%d", global->cache->icpport);
271 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
272 xml_list = xml_list_add(xml_list, "</icpport>", XML_TAG);
273
274 xml_list = xml_list_add(xml_list, "<icptimeout>", XML_TAG);
275 snprintf(buf, sizeof(buf), "%d", global->cache->icptimeout);
276 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
277 xml_list = xml_list_add(xml_list, "</icptimeout>", XML_TAG);
278
279 xml_list = xml_list_add(xml_list, "<sbalancemethod>", XML_TAG);
280 xml_list = xml_list_add(xml_list, (global->cache->sbalancemethod == SBALANCE_FILLSIZE) ? "fillsize" : "fillpercent", XML_VALUE);
281 xml_list = xml_list_add(xml_list, "</sbalancemethod>", XML_TAG);
282
283 for (sl = global->cache->stores; sl; sl = sl->next) {
284 xml_list = xml_list_add(xml_list, "<store>", XML_TAG);
285
286 xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
287 xml_list = xml_list_add(xml_list, (sl->enabled == TRUE) ? "true" : "false", XML_VALUE);
288 xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
289
290 if (sl->comment != NULL) {
291 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
292 ptr = string_to_xml(sl->comment);
293 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
294 xfree(ptr);
295 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
296 }
297
298 if (sl->profiles != NULL) {
299 xml_list = xml_list_add(xml_list, "<profiles>", XML_TAG);
300 ptr = array_merge(sl->profiles, ',');
301 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
302 xfree(ptr);
303 xml_list = xml_list_add(xml_list, "</profiles>", XML_TAG);
304 }
305
306 if (sl->path != NULL) {
307 xml_list = xml_list_add(xml_list, "<path>", XML_TAG);
308 ptr = string_to_xml(sl->path);
309 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
310 xfree(ptr);
311 xml_list = xml_list_add(xml_list, "</path>", XML_TAG);
312 }
313
314 xml_list = xml_list_add(xml_list, "<maxdisksize>", XML_TAG);
315 snprintf(buf, sizeof(buf), "%u", sl->maxdisksize);
316 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
317 xml_list = xml_list_add(xml_list, "</maxdisksize>", XML_TAG);
318
319 xml_list = xml_list_add(xml_list, "<diskextra>", XML_TAG);
320 snprintf(buf, sizeof(buf), "%u", sl->diskextra);
321 xml_list = xml_list_add(xml_list, buf, XML_VALUE);
322 xml_list = xml_list_add(xml_list, "</diskextra>", XML_TAG);
323
324 xml_list = xml_list_add(xml_list, "</store>", XML_TAG);
325 }
326
327 xml_list = xml_list_add(xml_list, "</cache>", XML_TAG);
328
329 pthread_mutex_unlock(&global->cache->lock);
330
331 return xml_list;
332 }
333
global_cache_insert(char * maxmemsize,char * memextra,char * maxage,char * validate,char * lmfactor,char * maxsize,char * minage,char * minsize,char * maxwaitsize,char * prefetchwindow,char * icpport,char * icptimeout,char * sbalancemethod)334 void global_cache_insert(char *maxmemsize, char *memextra, char *maxage, char *validate, char *lmfactor, char *maxsize, char *minage, char *minsize, char *maxwaitsize, char *prefetchwindow, char *icpport, char *icptimeout, char *sbalancemethod)
335 {
336 if (maxmemsize != NULL) {
337 global->cache->maxmemsize = 0;
338 if (strcmp(maxmemsize, ""))
339 global->cache->maxmemsize = string_to_size(maxmemsize);
340 }
341
342 if (memextra != NULL) {
343 global->cache->memextra = 0;
344 if (strcmp(memextra, ""))
345 global->cache->memextra = string_to_size(memextra);
346 }
347
348 if (maxage != NULL) {
349 global->cache->maxage = 0;
350
351 if (strcmp(maxage, ""))
352 global->cache->maxage = atoi(maxage);
353 }
354
355 if (validate != NULL) {
356 global->cache->validate = 0;
357
358 if (strcmp(validate, ""))
359 global->cache->validate = atoi(validate);
360 }
361
362 if (lmfactor != NULL) {
363 global->cache->lmfactor = 0;
364
365 if (strcmp(lmfactor, ""))
366 global->cache->lmfactor = atoi(lmfactor);
367 }
368
369 if (maxsize != NULL) {
370 global->cache->maxsize = 0;
371
372 if (strcmp(maxsize, ""))
373 global->cache->maxsize = string_to_size(maxsize);
374 }
375
376 if (minage != NULL) {
377 global->cache->minage = 0;
378
379 if (strcmp(minage, ""))
380 global->cache->minage = atoi(minage);
381 }
382
383 if (minsize != NULL) {
384 global->cache->minsize = 0;
385
386 if (strcmp(minsize, ""))
387 global->cache->minsize = string_to_size(minsize);
388 }
389
390 if (maxwaitsize != NULL) {
391 global->cache->maxwaitsize = 0;
392
393 if (strcmp(maxwaitsize, ""))
394 global->cache->maxwaitsize = string_to_size(maxwaitsize);
395 }
396
397 if (prefetchwindow != NULL) {
398 global->cache->prefetchwindow = 10;
399
400 if (strcmp(prefetchwindow, ""))
401 global->cache->prefetchwindow = atoi(prefetchwindow);
402 }
403
404 if (icpport != NULL) {
405 global->cache->icpport = 0;
406
407 if (strcmp(icpport, ""))
408 global->cache->icpport = atoi(icpport);
409 }
410
411 if (icptimeout != NULL) {
412 global->cache->icptimeout = ICP_TIMEOUT;
413
414 if (strcmp(icptimeout, ""))
415 global->cache->icptimeout = atoi(icptimeout);
416 }
417
418 if (sbalancemethod != NULL) {
419 global->cache->sbalancemethod = SBALANCE_FILLSIZE;
420
421 if (!strcasecmp(sbalancemethod, "fillsize"))
422 global->cache->sbalancemethod = SBALANCE_FILLSIZE;
423 else if (!strcasecmp(sbalancemethod, "fillpercent"))
424 global->cache->sbalancemethod = SBALANCE_FILLPERCENT;
425 }
426 }
427
stores_list_new(struct cache_store * x)428 struct cache_store *stores_list_new(struct cache_store *x)
429 {
430
431 if (x == NULL) {
432 x = xmalloc(sizeof(struct cache_store));
433 x->prev = NULL;
434 } else {
435 while (x->next != NULL)
436 x = x->next;
437 x->next = xmalloc(sizeof(struct cache_store));
438 x->next->prev = x;
439 x = x->next;
440 }
441
442 x->enabled = TRUE;
443 x->next = NULL;
444 x->comment = NULL;
445 x->profiles = NULL;
446 x->path = NULL;
447 x->journalentries = 0;
448 x->journalfd = -1;
449 x->diskentries = 0;
450 x->disksize = x->maxdisksize = x->diskextra = 0;
451
452
453 return x;
454 }
455
stores_list_delete(struct cache_store * x)456 struct cache_store *stores_list_delete(struct cache_store *x)
457 {
458 struct cache_store *start = x;
459
460 while (start->prev != NULL)
461 start = start->prev;
462
463 if (x->next != NULL)
464 x->next->prev = x->prev;
465 if (x->prev != NULL)
466 x->prev->next = x->next;
467 else
468 start = x->next;
469
470 if (x->profiles != NULL)
471 array_free(x->profiles);
472 FREE_AND_NULL(x->comment);
473 FREE_AND_NULL(x->path);
474
475 xfree(x);
476
477 return start;
478 }
479
stores_list_free(struct cache_store * x)480 void stores_list_free(struct cache_store *x)
481 {
482 struct cache_store *tmp;
483
484 while (x != NULL) {
485 tmp = x->next;
486
487 if (x->profiles != NULL)
488 array_free(x->profiles);
489 FREE_AND_NULL(x->comment);
490 FREE_AND_NULL(x->path);
491
492 x = tmp;
493 }
494 }
495
stores_list_insert(struct cache_store * x,char * comment,char * profiles,char * path,char * maxdisksize,char * diskextra)496 void stores_list_insert(struct cache_store *x, char *comment, char *profiles, char *path, char *maxdisksize, char *diskextra)
497 {
498 if (comment != NULL) {
499 FREE_AND_NULL(x->comment);
500
501 if (strcmp(comment, ""))
502 x->comment = xstrdup(comment);
503 }
504
505 if (profiles != NULL) {
506 if (x->profiles != NULL)
507 array_free(x->profiles);
508
509 if (strcmp(profiles, ""))
510 x->profiles = string_break(profiles, ',');
511 else
512 x->profiles = NULL;
513 }
514
515 if (path != NULL) {
516 FREE_AND_NULL(x->path);
517
518 if (strcmp(path, ""))
519 x->path = xstrdup(path);
520 }
521
522 if (maxdisksize != NULL) {
523 x->maxdisksize = 0;
524
525 if (strcmp(maxdisksize, ""))
526 x->maxdisksize = string_to_size(maxdisksize);
527 }
528
529 if (diskextra != NULL) {
530 x->diskextra = 0;
531
532 if (strcmp(diskextra, ""))
533 x->diskextra = string_to_size(diskextra);
534 }
535 }
536
select_store(CONNECTION * connection,CACHE * cache,CACHEMAP * cachemap)537 void select_store(CONNECTION * connection, CACHE * cache, CACHEMAP * cachemap)
538 {
539 int ptmp;
540 size_t lowest = ~0;
541 struct cache_store *selected = NULL, *stores;
542
543 ASSERT(connection != NULL);
544
545 for (stores = cache->stores; stores; stores = stores->next) {
546 if (stores->enabled == FALSE)
547 continue;
548
549 if (!profile_find(connection->profiles, stores->profiles))
550 continue;
551
552 /* check if the entry is properly configured, so assumptions can be made later */
553 if (stores->path == NULL || stores->maxdisksize == 0)
554 continue;
555
556 switch (cache->sbalancemethod) {
557 case SBALANCE_FILLSIZE:
558 if (stores->disksize < lowest) {
559 lowest = stores->disksize;
560 selected = stores;
561 }
562 break;
563 case SBALANCE_FILLPERCENT:
564 ptmp = (int) (((float) stores->disksize / (float) stores->maxdisksize) * 100.0);
565 if (ptmp < lowest) {
566 lowest = ptmp;
567 selected = stores;
568 }
569 break;
570 }
571
572 }
573
574 cachemap->store = selected;
575 }
576
stores_update(CACHE * cache)577 void stores_update(CACHE * cache)
578 {
579 int i;
580 struct cache_store *stores;
581 CACHEMAP *cachemap;
582 struct CACHEMAPLIST_T *cl, *tmp;
583
584 /* this routine is called whenever the cache disk store configuration is changed, it will resynchronize everything
585 in the cache hash table. */
586 for (i = 0; i < cache->hashsize; i++) {
587 for (cl = cache->cachemap[i]; cl; cl = tmp) {
588 tmp = cl->next;
589 cachemap = cl->cachemap;
590
591 if (cachemap->store != NULL) {
592 for (stores = cache->stores; stores && cachemap->store != stores; stores = stores->next);
593 if (stores == NULL || stores->path == NULL) {
594 /* this cachemap's filesystem store information doesn't exist anymore */
595
596 if (!(cachemap->flags & CACHE_MEM)) {
597 cache_hash_remove(cache, cachemap);
598 cache_free(cache, cachemap);
599 } else {
600 /* it's mapped in memory, so it may still be useful. */
601 cl->cachemap->store = NULL;
602 cl->cachemap->flags &= ~CACHE_DISK;
603 }
604 }
605 }
606 }
607 }
608
609 for (stores = global->cache->stores; stores; stores = stores->next) {
610 if (stores->journalfd != -1) {
611 close(stores->journalfd);
612 stores->journalfd = -1;
613 }
614
615 if (stores->path == NULL)
616 continue;
617
618 stores->diskentries = stores->disksize = 0;
619
620 /* re-reading the journals will reassociate each cache object in the hash table with a cache disk store. */
621 cache_journal_read(global->cache, stores);
622 cache_journal_write(global->cache, stores);
623 }
624 }
625
cache_init()626 CACHE *cache_init()
627 {
628 CACHE *cache;
629
630 cache = xmalloc(sizeof(CACHE));
631
632 pthread_mutex_init(&cache->lock, NULL);
633
634 cache->enabled = FALSE;
635 cache->id = 0;
636 cache->sbalancemethod = SBALANCE_FILLSIZE;
637 cache->violaterfc = FALSE;
638 cache->hashsize = CACHEMAP_HASH_SIZE;
639 cache->memsize = cache->maxmemsize = cache->memextra = 0;
640 cache->minage = cache->maxage = 0;
641 cache->lmfactor = 0;
642 cache->validate = 0;
643 cache->minsize = cache->maxsize = 0;
644 cache->mementries = 0;
645 cache->hit = cache->miss = 0;
646 cache->maxwaitsize = 0;
647 cache->prefetchwindow = 10;
648 cache->lastclean = 0;
649 cache->icpport = 0;
650 cache->icptimeout = ICP_TIMEOUT;
651 cache->stores = NULL;
652
653 cache->cachemap = xmalloc(sizeof(struct CACHEMAPLIST_T *) * cache->hashsize);
654 memset(cache->cachemap, 0, sizeof(struct CACHEMAPLIST_T *) * cache->hashsize);
655
656 return cache;
657 }
658
cache_hash_find(CACHE * cache,char * key)659 CACHEMAP *cache_hash_find(CACHE * cache, char *key)
660 {
661 unsigned int k;
662 struct CACHEMAPLIST_T *cachemaplist;
663 CACHEMAP *cachemap = NULL;
664
665 k = hash_key(cache->hashsize, key);
666
667 cachemaplist = cache->cachemap[k];
668
669 while (cachemaplist && strcmp(cachemaplist->cachemap->key, key))
670 cachemaplist = cachemaplist->next;
671
672 if (cachemaplist != NULL)
673 cachemap = cachemaplist->cachemap;
674
675 return cachemap;
676 }
677
cache_filename(CACHE * cache,CACHEMAP * cachemap,char * buf,int len)678 int cache_filename(CACHE * cache, CACHEMAP * cachemap, char *buf, int len)
679 {
680 int x;
681 unsigned char k;
682 char *key = cachemap->key;
683
684 k = hash_key(256, cachemap->key);
685 x = snprintf(buf, len, "%s/%.2x/", cachemap->store->path, k);
686 for (; x < len - 1 && *key; x++, key++)
687 buf[x] = (*key == '/') ? '\\' : *key;
688 buf[x] = '\0';
689
690 return k;
691 }
692
cache_hash_add(CACHE * cache,CACHEMAP * cachemap)693 void cache_hash_add(CACHE * cache, CACHEMAP * cachemap)
694 {
695 unsigned int k;
696 struct CACHEMAPLIST_T *cachemaplist;
697
698 k = hash_key(cache->hashsize, cachemap->key);
699
700 if (cache->cachemap[k] == NULL) {
701 cache->cachemap[k] = xmalloc(sizeof(struct CACHEMAPLIST_T));
702 cache->cachemap[k]->cachemap = cachemap;
703 cache->cachemap[k]->next = cache->cachemap[k]->prev = NULL;
704 } else {
705 for (cachemaplist = cache->cachemap[k]; strcmp(cachemaplist->cachemap->key, cachemap->key) && cachemaplist->next; cachemaplist = cachemaplist->next);
706 ASSERT(cachemaplist->next == NULL);
707
708 cachemaplist->next = xmalloc(sizeof(struct CACHEMAPLIST_T));
709 cachemaplist->next->cachemap = cachemap;
710 cachemaplist->next->prev = cachemaplist;
711 cachemaplist->next->next = NULL;
712 }
713 }
714
cache_hash_remove(CACHE * cache,CACHEMAP * cachemap)715 void cache_hash_remove(CACHE * cache, CACHEMAP * cachemap)
716 {
717 unsigned int k;
718 struct CACHEMAPLIST_T *cachemaplist;
719
720 k = hash_key(cache->hashsize, cachemap->key);
721
722 for (cachemaplist = cache->cachemap[k]; cachemaplist && strcmp(cachemaplist->cachemap->key, cachemap->key); cachemaplist = cachemaplist->next);
723 if (cachemaplist != NULL) {
724 if (cachemaplist->next != NULL)
725 cachemaplist->next->prev = cachemaplist->prev;
726 if (cachemaplist->prev != NULL)
727 cachemaplist->prev->next = cachemaplist->next;
728 else
729 cache->cachemap[k] = cachemaplist->next;
730
731 xfree(cachemaplist);
732 } else
733 ASSERT(0);
734 }
735
736
737
cache_open(CONNECTION * connection,CACHE * cache,char * key,char * header,int flags)738 CACHEMAP *cache_open(CONNECTION * connection, CACHE * cache, char *key, char *header, int flags)
739 {
740 int fd;
741 time_t utctime;
742 char buf[1024], *ptr;
743 CACHEMAP *cachemap;
744 struct stat st;
745
746 pthread_mutex_lock(&cache->lock);
747
748 if (cache->enabled == FALSE) {
749 pthread_mutex_unlock(&cache->lock);
750
751 return NULL;
752 }
753
754 utctime = utc_time(NULL);
755
756 cachemap = cache_hash_find(cache, key);
757
758 if (cachemap != NULL) {
759 if ((cachemap->flags & CACHE_PREFETCHED) && utctime - cachemap->mtime > cache->prefetchwindow)
760 cachemap->flags &= ~CACHE_PREFETCHED;
761
762 if (flags & CACHE_WRITING) {
763 /* this will solve a race condition where a URL gets added to the prefetch
764 queue more than once and several threads try to prefetch it at
765 about the same time. */
766 if (cachemap->refcount != 0 || (cachemap->flags & CACHE_PREFETCHED)) {
767 if (cachemap->flags & CACHE_PREFETCHED)
768 putlog(MMLOG_DEBUG, "attempted to recreate cached file within prefetch window");
769
770 cachemap = NULL;
771
772 goto out;
773 }
774
775 putlog(MMLOG_DEBUG, "recreating cache file");
776
777 /* recreate existing cache file */
778 cache_invalidate_unlocked(cache, cachemap);
779
780 cachemap = cachemap_new(cache);
781 cachemap->key = xstrdup(key);
782 cachemap->size = cachemap->realsize = cachemap->offset = 0;
783 cachemap->mtime = utctime;
784 cachemap->flags = flags;
785 cachemap->refcount = 1;
786
787 cache_hash_add(cache, cachemap);
788 } else {
789 if (cachemap->flags & CACHE_INVALID) {
790 if (cachemap->flags & CACHE_INVALID)
791 putlog(MMLOG_DEBUG, "cache object is invalid");
792
793 cachemap = NULL;
794
795 goto out;
796 }
797
798 if (cachemap->flags & CACHE_WRITING) {
799 /* wait for thread writing the cache file to complete.
800 this isn't a very good way to do things, I would rather
801 be able to read from the cache as it is being written;
802 but this becomes very complex with threading, and I don't
803 think it's worth the trouble to handle rare cases like this */
804
805 if ((cachemap->header->content_length != -1 && cachemap->header->content_length > cache->maxwaitsize) || cachemap->size > cache->maxwaitsize) {
806 /* file is too large to wait on */
807 putlog(MMLOG_DEBUG, "cache file too large to wait on");
808
809 cachemap = NULL;
810
811 goto out;
812 }
813
814 /* increase refcount incase thread downloading file
815 tries to invalidate it */
816 cachemap->refcount++;
817
818 pthread_cond_wait(&cachemap->writecompletecond, &cache->lock);
819
820 if (cachemap->flags & CACHE_INVALID) {
821 cache_invalidate_unlocked(cache, cachemap);
822
823 cachemap = NULL;
824
825 goto out;
826 }
827
828 cachemap->refcount--;
829
830 /* did we stop waiting because it was too large ? */
831 if (cachemap->size > cache->maxwaitsize) {
832 putlog(MMLOG_DEBUG, "stopped waiting on large cache file");
833
834 cachemap = NULL;
835
836 goto out;
837 }
838
839 putlog(MMLOG_DEBUG, "waited for cache file that was being written");
840 }
841
842 if (!(cachemap->flags & CACHE_MEM) && cachemap->store != NULL) {
843 ASSERT(cachemap->flags & CACHE_DISK);
844 ASSERT(cachemap->data == NULL);
845 ASSERT(cachemap->fd == -1);
846 ASSERT(cachemap->refcount == 0);
847
848 cache_filename(cache, cachemap, buf, sizeof(buf));
849
850 fd = open(buf, O_RDONLY);
851 if (fd != -1) {
852 fstat(fd, &st);
853 ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
854
855 if (ptr != MAP_FAILED) {
856 cachemap->mtime = st.st_mtime + gmtoffset;
857 cachemap->atime = st.st_atime + gmtoffset;
858
859 if (cachemap->size != st.st_size) {
860 putlog(MMLOG_CACHE, "file meta information didn't match journaled data");
861
862 /* compensate for unexpected change in file size */
863 cachemap->store->disksize += (int) (st.st_size - cachemap->size);
864 cachemap->size = cachemap->realsize = st.st_size;
865
866 /* add journal entry with updated file information */
867 cache_journal_add(cache, cachemap);
868 } else
869 cachemap->size = cachemap->realsize = st.st_size;
870
871 cachemap->data = ptr;
872 cachemap->flags |= CACHE_MEM;
873
874 cache->memsize += cachemap->size;
875 cache->mementries++;
876
877 #ifdef HAVE_MADVISE
878 /* ask the kernel to asyncronously read the file from disk so it's cached
879 by the time we need it. */
880 madvise(cachemap->data, cachemap->size, MADV_WILLNEED);
881 #endif /* HAVE_MADVISE */
882 }
883
884 close(fd);
885 }
886 }
887
888 if (!(cachemap->flags & CACHE_MEM)) {
889 cache_invalidate_unlocked(cache, cachemap);
890 cachemap = NULL;
891
892 goto out;
893 }
894
895 cachemap->refcount++;
896 }
897 } else if (flags & CACHE_WRITING) {
898 /* new cache item */
899 cachemap = cachemap_new(cache);
900 cachemap->key = xstrdup(key);
901 cachemap->size = cachemap->realsize = cachemap->offset = 0;
902 cachemap->mtime = utctime;
903 cachemap->flags = flags;
904 cachemap->refcount = 1;
905
906 cache_hash_add(cache, cachemap);
907 }
908
909 if (cachemap != NULL) {
910 cachemap->atime = utctime;
911
912 if (!(cachemap->flags & CACHE_WRITING)) {
913 if (cachemap->header == NULL) {
914 /* refcount must be 1 at this point, so modifying any cachemap
915 member is safe */
916 cachemap->offset = header_length(cachemap->data, cachemap->size);
917 if (cachemap->offset != 0)
918 cachemap->header = http_header_parse_response(cachemap->data);
919 }
920 } else {
921 select_store(connection, cache, cachemap);
922
923 /* set header first or we open up a race condition since we unlock
924 in cache_add */
925 cachemap->header = http_header_parse_response(header);
926 cachemap->offset = strlen(header);
927
928 if (cachemap->header->content_length != -1 && cachemap->header->content_length > cache->maxsize) {
929 putlog(MMLOG_CACHE, "file too large to cache (size: %d)", cachemap->header->content_length);
930
931 cache_invalidate_unlocked(cache, cachemap);
932
933 cachemap = NULL;
934
935 goto out;
936 }
937
938 /* NOTE: cache_add_unlocked unlocks the mutex to relieve contention...
939 there may be race conditions calling it from here if you're not careful! */
940 cache_add_unlocked(cache, cachemap, header, strlen(header));
941
942 putlog(MMLOG_DEBUG, "cache header offset: %d", cachemap->offset);
943 }
944
945 if (cachemap->offset == 0 || cachemap->header == NULL || !cacheable(cachemap->header)) {
946 /* this shouldn't happen, it may be a sign that the cache file was corrupted */
947 cache_invalidate_unlocked(cache, cachemap);
948
949 cachemap = NULL;
950
951 putlog(MMLOG_CACHE, "corrupt header: %s", key);
952
953 goto out;
954 }
955
956 if (!(cachemap->flags & CACHE_WRITING))
957 cache_check_header(cache, cachemap);
958
959 if (!(flags & CACHE_OFFLINE) && (cachemap->flags & CACHE_VIOLATION) && cache->violaterfc == FALSE) {
960 cache_close_unlocked(cache, cachemap);
961 cachemap = NULL;
962 }
963
964 }
965
966 out:
967
968 if (cache->memsize > cache->maxmemsize || (cachemap != NULL && cachemap->store != NULL && cachemap->store->disksize > cachemap->store->maxdisksize))
969 cache_clean(cache);
970
971 pthread_mutex_unlock(&cache->lock);
972
973 return cachemap;
974 }
975
cache_close(CACHE * cache,CACHEMAP * cachemap)976 void cache_close(CACHE * cache, CACHEMAP * cachemap)
977 {
978 pthread_mutex_lock(&cache->lock);
979 cache_close_unlocked(cache, cachemap);
980 pthread_mutex_unlock(&cache->lock);
981 }
982
cache_close_unlocked(CACHE * cache,CACHEMAP * cachemap)983 void cache_close_unlocked(CACHE * cache, CACHEMAP * cachemap)
984 {
985 if (cachemap->flags & CACHE_WRITING) {
986 cachemap->header->content_length = cachemap->size - cachemap->offset;
987
988 if (cachemap->size != cachemap->realsize)
989 cache_resize(cache, cachemap, 0);
990
991 msync(cachemap->data, cachemap->realsize, MS_ASYNC);
992
993 if (cachemap->fd != -1)
994 close(cachemap->fd);
995
996 cachemap->fd = -1;
997 }
998
999 cachemap->refcount--;
1000
1001 if (cachemap->flags & CACHE_INVALID) {
1002 if (cachemap->refcount == 0)
1003 cache_invalidate_unlocked(cache, cachemap);
1004 } else if (cachemap->flags & CACHE_WRITING) {
1005 if (cachemap->size == 0 && cachemap->size < cache->minsize) {
1006 if (cachemap->size < cache->minsize)
1007 putlog(MMLOG_CACHE, "file too small");
1008
1009 cache_invalidate_unlocked(cache, cachemap);
1010 } else {
1011 cachemap->flags &= ~CACHE_WRITING;
1012
1013 /* cache_touch_unlocked does the journal update */
1014 cache_touch_unlocked(cache, cachemap, TRUE);
1015
1016 pthread_cond_broadcast(&cachemap->writecompletecond);
1017 }
1018 } else
1019 cache_touch_unlocked(cache, cachemap, FALSE);
1020 }
1021
cache_add(CACHE * cache,CACHEMAP * cachemap,void * buf,size_t len)1022 int cache_add(CACHE * cache, CACHEMAP * cachemap, void *buf, size_t len)
1023 {
1024 int ret;
1025
1026 pthread_mutex_lock(&cache->lock);
1027
1028 ret = cache_add_unlocked(cache, cachemap, buf, len);
1029
1030 pthread_mutex_unlock(&cache->lock);
1031
1032 return ret;
1033 }
1034
cache_add_unlocked(CACHE * cache,CACHEMAP * cachemap,void * buf,size_t len)1035 int cache_add_unlocked(CACHE * cache, CACHEMAP * cachemap, void *buf, size_t len)
1036 {
1037 int x;
1038 size_t ret;
1039 char path[1024], file[1024], *key;
1040
1041 if (cachemap->flags & CACHE_INVALID)
1042 return -1;
1043
1044 key = cachemap->key;
1045
1046 if (!(cachemap->flags & CACHE_MEM)) {
1047 if (cachemap->store != NULL) {
1048 cache_filename(cache, cachemap, file, sizeof(file));
1049
1050 x = hash_key(256, key);
1051 snprintf(path, sizeof(path), "%s/%.2x", cachemap->store->path, x);
1052
1053 mkdir(path, 0755);
1054
1055 cachemap->fd = open(file, O_RDWR | O_CREAT, 0644);
1056 if (cachemap->fd != -1) {
1057 cachemap->store->diskentries++;
1058 cachemap->flags |= CACHE_DISK;
1059
1060 cache_journal_add(cache, cachemap);
1061 } else
1062 cachemap->store = NULL;
1063 }
1064
1065 ret = cache_resize(cache, cachemap, len);
1066 if (ret == 0)
1067 goto error;
1068
1069 memcpy(cachemap->data, buf, len);
1070 } else {
1071 ret = cache_resize(cache, cachemap, len);
1072 if (ret == 0)
1073 goto error;
1074
1075 memcpy(cachemap->data + (cachemap->size - len), buf, len);
1076
1077 }
1078
1079
1080 cache->memsize += len;
1081 if (cachemap->flags & CACHE_DISK)
1082 cachemap->store->disksize += len;
1083
1084 if ((cachemap->store != NULL && cachemap->store->disksize > cachemap->store->maxdisksize) || cache->memsize > cache->maxmemsize)
1085 cache_clean(cache);
1086
1087 if (cache->maxsize != 0 && cachemap->size > cache->maxsize) {
1088 putlog(MMLOG_CACHE, "max size exceeded (size: %u)", cachemap->size);
1089
1090 goto error;
1091 }
1092
1093 if (cachemap->size > cache->maxwaitsize)
1094 pthread_cond_broadcast(&cachemap->writecompletecond);
1095
1096 return len;
1097
1098 error:
1099 /* we don't want to free it yet */
1100 cachemap->refcount++;
1101 cache_invalidate_unlocked(cache, cachemap);
1102
1103 pthread_cond_broadcast(&cachemap->writecompletecond);
1104
1105 return -1;
1106 }
1107
cache_scandisk(CACHE * cache,struct cache_store * store)1108 void cache_scandisk(CACHE * cache, struct cache_store *store)
1109 {
1110 int x;
1111 char buf[1024], buf2[1024], *ptr;
1112 DIR *base, *subdir;
1113 CACHEMAP *cachemap;
1114 struct dirent *basedirent, *subdirent;
1115 struct stat st;
1116 URL *url;
1117
1118 base = opendir(store->path);
1119 if (base == NULL)
1120 return;
1121
1122 putlog(MMLOG_CACHE, "scanning cache directory structure in %s", store->path);
1123
1124 while ((basedirent = readdir(base)) != NULL) {
1125 if (!strcmp(basedirent->d_name, ".") || !strcmp(basedirent->d_name, ".."))
1126 continue;
1127
1128 snprintf(buf, sizeof(buf), "%s/%s", store->path, basedirent->d_name);
1129 subdir = opendir(buf);
1130 if (subdir != NULL) {
1131 while ((subdirent = readdir(subdir)) != NULL) {
1132 snprintf(buf, sizeof(buf), "%s/%s/%s", store->path, basedirent->d_name, subdirent->d_name);
1133 x = stat(buf, &st);
1134 if (x != -1 && S_ISREG(st.st_mode)) {
1135 s_strncpy(buf2, subdirent->d_name, sizeof(buf2));
1136 for (x = 0; buf2[x]; x++)
1137 if (buf2[x] == '\\')
1138 buf2[x] = '/';
1139
1140
1141 /* this makes sure it's a valid URL */
1142 url = url_parse(buf2);
1143 if (url != NULL) {
1144 ptr = url_create(url);
1145 url_free(url);
1146 } else {
1147 putlog(MMLOG_CACHE, "%s doesn't appear to be a valid cache file", buf);
1148 continue;
1149 }
1150
1151 cachemap = cache_hash_find(cache, ptr);
1152 if (cachemap != NULL) {
1153 /* this can happen if the unlink thread didn't reach this entry before the proxy
1154 server was killed and the file was cached again, or if this directory
1155 was used for another instance of the proxy. */
1156 putlog(MMLOG_CACHE, "duplicate of %s from %s found in %s", ptr, (cachemap->store->path != NULL) ? cachemap->store->path : "(memory)", store->path);
1157
1158 /* this could happen if scandisk is called twice on the same directory */
1159 if (cachemap->store == store) {
1160 xfree(ptr);
1161
1162 continue;
1163 }
1164
1165 /* keep other copy if it's in use or newer */
1166 if (cachemap->refcount != 0 || cachemap->mtime > st.st_mtime + gmtoffset) {
1167 unlink_list_add(buf);
1168
1169 xfree(ptr);
1170
1171 continue;
1172 }
1173
1174 if (cachemap->flags & CACHE_MEM)
1175 cache_mem_delete(cache, cachemap);
1176 if (cachemap->flags & CACHE_DISK)
1177 cache_disk_delete(cache, cachemap);
1178 }
1179
1180 if (cachemap == NULL) {
1181 cachemap = cachemap_new(cache);
1182 cachemap->key = ptr;
1183 cache_hash_add(cache, cachemap);
1184 } else
1185 xfree(ptr);
1186
1187 cachemap->store = store;
1188 cachemap->size = cachemap->realsize = st.st_size;
1189 cachemap->mtime = st.st_mtime + gmtoffset;
1190 cachemap->atime = st.st_atime + gmtoffset;
1191 cachemap->flags = CACHE_DISK;
1192
1193 store->disksize += st.st_size;
1194 store->diskentries++;
1195 }
1196 }
1197
1198 closedir(subdir);
1199 }
1200 }
1201
1202 closedir(base);
1203
1204 return;
1205 }
1206
cache_clean(CACHE * cache)1207 void cache_clean(CACHE * cache)
1208 {
1209 int i;
1210 unsigned int memsizegoal, disksizegoal;
1211 time_t oldest = 0, newest = 0, threshold, increment, utctime;
1212 struct CACHEMAPLIST_T *cachemaplist, *tmp;
1213 CACHEMAP *cachemap;
1214
1215 utctime = utc_time(NULL);
1216
1217 if (cache->lastclean != 0 && cache->lastclean + CLEAN_INTERVAL > utctime)
1218 return;
1219
1220 cache->lastclean = utctime;
1221
1222 putlog(MMLOG_CACHE, "cleaning cache");
1223
1224 for (i = 0; i < cache->hashsize; i++) {
1225 for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = cachemaplist->next) {
1226 if (cachemaplist->cachemap->refcount != 0)
1227 continue;
1228
1229 if (cachemaplist->cachemap->atime > newest || newest == 0)
1230 newest = cachemaplist->cachemap->atime;
1231 if (cachemaplist->cachemap->atime < oldest || oldest == 0)
1232 oldest = cachemaplist->cachemap->atime;
1233 }
1234 }
1235
1236 /* oops, nothing we can free */
1237 if (newest == 0)
1238 return;
1239
1240 increment = (newest - oldest) / CACHE_INCREMENT_FRACTION + 1;
1241 threshold = oldest + increment;
1242
1243 if (cache->memsize > cache->maxmemsize)
1244 memsizegoal = (cache->memextra > cache->maxmemsize) ? 0 : cache->maxmemsize - cache->memextra;
1245 else
1246 memsizegoal = cache->maxmemsize;
1247
1248 while (threshold <= newest + increment) {
1249 for (i = 0; i < cache->hashsize; i++) {
1250 for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = tmp) {
1251 tmp = cachemaplist->next;
1252 cachemap = cachemaplist->cachemap;
1253
1254 if (cachemap->refcount != 0)
1255 continue;
1256
1257 if (cachemap->atime > threshold)
1258 continue;
1259
1260 ASSERT(cachemap->flags & (CACHE_DISK | CACHE_MEM));
1261 ASSERT(!(cachemap->flags & CACHE_DISK) || cachemap->store != NULL);
1262
1263 if (cachemap->flags & CACHE_DISK) {
1264 if (cachemap->store->disksize > cachemap->store->maxdisksize)
1265 disksizegoal = (cachemap->store->diskextra > cachemap->store->maxdisksize) ? 0 : cachemap->store->maxdisksize - cachemap->store->diskextra;
1266 else
1267 disksizegoal = cachemap->store->maxdisksize;
1268
1269 if (cachemap->store->disksize > disksizegoal) {
1270 cache_invalidate_unlocked(cache, cachemap);
1271 continue;
1272 }
1273 }
1274
1275 if ((cachemap->flags & CACHE_MEM) && cache->memsize > memsizegoal) {
1276 if (!(cachemap->flags & CACHE_DISK))
1277 cache_hash_remove(cache, cachemap);
1278
1279 cache_mem_delete(cache, cachemap);
1280 }
1281 }
1282 }
1283
1284 /* keep increasing the threshold until enough memory can be free'd */
1285 threshold += increment;
1286 }
1287 }
1288
cacheable(HEADER * header)1289 int cacheable(HEADER * header)
1290 {
1291 if (header->type == HTTP_RESP) {
1292 if (header->code != 200)
1293 return FALSE;
1294 } else if (header->type == HTTP_PROXY) {
1295 if (strcasecmp(header->method, "GET"))
1296 return FALSE;
1297 if (header->authorization != NULL)
1298 return FALSE;
1299 if (header->username != NULL && strcasecmp(header->username, ANONLOGIN))
1300 return FALSE;
1301 if (header->range != NULL)
1302 return FALSE;
1303 } else
1304 return FALSE;
1305
1306 return TRUE;
1307 }
1308
1309 /*
1310 check if cache file needs validating based on request headers
1311 */
cache_check(CACHE * cache,HEADER * header)1312 void cache_check(CACHE * cache, HEADER * header)
1313 {
1314 int x, expired = FALSE, i;
1315 time_t utctime = utc_time(NULL);
1316 char buf[1024], **args;
1317 CACHEMAP *cachemap = NULL;
1318
1319 snprintf(buf, sizeof(buf), "%s://%s:%d%s", header->proto, header->host, header->port, header->file);
1320 cachemap = cache_open(NULL, cache, buf, NULL, CACHE_OFFLINE);
1321
1322 if (cachemap == NULL)
1323 return;
1324
1325 pthread_mutex_lock(&cache->lock);
1326
1327 if (cachemap->flags & CACHE_PREFETCHED)
1328 goto out;
1329
1330 if (cachemap->flags & CACHE_EXPIRED)
1331 expired = TRUE;
1332
1333 if (header->cache_control != NULL) {
1334 putlog(MMLOG_DEBUG, "request cache-control header: %s", header->cache_control);
1335
1336 args = string_break(header->cache_control, ',');
1337
1338 for (x = 0; args[x]; x++) {
1339 if (!strcasecmp(args[x], "no-cache") || !strcasecmp(args[x], "no-store"))
1340 expired = TRUE;
1341 else if (!strncasecmp(args[x], "max-age=", 8)) {
1342 /* file can't be any older than this */
1343 i = atoi(&args[x][8]);
1344
1345 if (utctime - cachemap->mtime > i)
1346 expired = TRUE;
1347 } else if (!strncasecmp(args[x], "min-fresh=", 10)) {
1348 /* file must be fresh for at least this long */
1349 i = atoi(&args[x][10]);
1350
1351 if ((cachemap->etime != 0 && utctime + i > cachemap->etime) || (cachemap->ftime != 0 && cachemap->etime == 0 && utctime + i > cachemap->ftime))
1352 expired = TRUE;
1353 } else if (!strncasecmp(args[x], "max-stale=", 10)) {
1354 /* file may be stale for this long */
1355 i = atoi(&args[x][10]);
1356
1357 if ((cachemap->etime != 0 && utctime - cachemap->etime <= i) || (cachemap->ftime != 0 && cachemap->etime == 0 && utctime - cachemap->ftime <= i))
1358 expired = FALSE;
1359
1360 }
1361
1362 xfree(args[x]);
1363 }
1364
1365 xfree(args);
1366 }
1367
1368 if (expired == TRUE)
1369 cachemap->flags |= CACHE_VALIDATE;
1370
1371 out:
1372
1373 pthread_mutex_unlock(&cache->lock);
1374 cache_close(cache, cachemap);
1375 }
1376
cache_to_filebuf(CACHEMAP * cachemap,int maxlen)1377 FILEBUF *cache_to_filebuf(CACHEMAP * cachemap, int maxlen)
1378 {
1379 FILEBUF *ret;
1380
1381 ret = filebuf_new();
1382
1383 filebuf_add(ret, cachemap->data + cachemap->offset, (maxlen < cachemap->size - cachemap->offset) ? maxlen : cachemap->size - cachemap->offset);
1384
1385 return ret;
1386 }
1387
cache_invalidate(CACHE * cache,CACHEMAP * cachemap)1388 void cache_invalidate(CACHE * cache, CACHEMAP * cachemap)
1389 {
1390 pthread_mutex_lock(&cache->lock);
1391
1392 cache_invalidate_unlocked(cache, cachemap);
1393
1394 pthread_mutex_unlock(&cache->lock);
1395 }
1396
cache_invalidate_unlocked(CACHE * cache,CACHEMAP * cachemap)1397 void cache_invalidate_unlocked(CACHE * cache, CACHEMAP * cachemap)
1398 {
1399 if (!(cachemap->flags & CACHE_INVALID)) {
1400 putlog(MMLOG_CACHE, "invalidated: %s", cachemap->key);
1401 cachemap->flags |= CACHE_INVALID;
1402 cache_journal_add(cache, cachemap);
1403 }
1404
1405 if (cachemap->refcount > 1) {
1406 cachemap->refcount--;
1407
1408 pthread_cond_broadcast(&cachemap->writecompletecond);
1409
1410 return;
1411 }
1412
1413 if (cachemap->flags & CACHE_MEM)
1414 cache_mem_delete(cache, cachemap);
1415 if (cachemap->flags & CACHE_DISK)
1416 cache_disk_delete(cache, cachemap);
1417
1418 cache_hash_remove(cache, cachemap);
1419
1420 cache_free(cache, cachemap);
1421 }
1422
1423
cache_disk_delete(CACHE * cache,CACHEMAP * cachemap)1424 void cache_disk_delete(CACHE * cache, CACHEMAP * cachemap)
1425 {
1426 char buf[1024];
1427
1428 ASSERT(cachemap->flags & CACHE_DISK);
1429 ASSERT(!(cachemap->flags & CACHE_MEM));
1430
1431 if (cachemap->store->path != NULL) {
1432 cache_filename(cache, cachemap, buf, sizeof(buf));
1433
1434 unlink_list_add(buf);
1435 }
1436
1437 cachemap->store->disksize -= cachemap->size;
1438 cachemap->store->diskentries--;
1439
1440 cachemap->flags &= ~CACHE_DISK;
1441 }
1442
cache_mem_delete(CACHE * cache,CACHEMAP * cachemap)1443 void cache_mem_delete(CACHE * cache, CACHEMAP * cachemap)
1444 {
1445 ASSERT((cachemap->flags & CACHE_MEM) && cachemap->data != NULL);
1446
1447 munmap(cachemap->data, cachemap->realsize);
1448 cache->memsize -= cachemap->size;
1449 cache->mementries--;
1450
1451 cachemap->flags &= ~CACHE_MEM;
1452 cachemap->data = NULL;
1453 }
1454
1455
1456
cache_touch(CACHE * cache,CACHEMAP * cachemap,int modified)1457 void cache_touch(CACHE * cache, CACHEMAP * cachemap, int modified)
1458 {
1459 pthread_mutex_lock(&cache->lock);
1460 cache_touch_unlocked(cache, cachemap, modified);
1461 pthread_mutex_unlock(&cache->lock);
1462 }
1463
cache_touch_unlocked(CACHE * cache,CACHEMAP * cachemap,int modified)1464 void cache_touch_unlocked(CACHE * cache, CACHEMAP * cachemap, int modified)
1465 {
1466 time_t utctime;
1467 char buf[1024];
1468 struct utimbuf utb;
1469
1470 utctime = utc_time(NULL);
1471
1472 cachemap->atime = utctime;
1473 if (modified == TRUE)
1474 cachemap->mtime = utctime;
1475
1476 if (cachemap->store != NULL) {
1477 cache_filename(cache, cachemap, buf, sizeof(buf));
1478
1479 utb.actime = cachemap->atime - gmtoffset;
1480 utb.modtime = cachemap->mtime - gmtoffset;
1481 utime(buf, &utb);
1482 }
1483
1484 cache_journal_add(cache, cachemap);
1485 }
1486
cache_free(CACHE * cache,CACHEMAP * cachemap)1487 void cache_free(CACHE * cache, CACHEMAP * cachemap)
1488 {
1489 if (cachemap->fd != -1)
1490 close(cachemap->fd);
1491
1492 if (cachemap->header != NULL)
1493 http_header_free(cachemap->header);
1494
1495 pthread_cond_destroy(&cachemap->writecompletecond);
1496
1497 xfree(cachemap->key);
1498 xfree(cachemap);
1499 }
1500
cache_flag_set(CACHE * cache,CACHEMAP * cachemap,int flag)1501 int cache_flag_set(CACHE * cache, CACHEMAP * cachemap, int flag)
1502 {
1503 int ret;
1504
1505 pthread_mutex_lock(&cache->lock);
1506 cachemap->flags |= flag;
1507 ret = cachemap->flags;
1508 pthread_mutex_unlock(&cache->lock);
1509
1510 return ret;
1511 }
1512
cache_flag_unset(CACHE * cache,CACHEMAP * cachemap,int flag)1513 int cache_flag_unset(CACHE * cache, CACHEMAP * cachemap, int flag)
1514 {
1515 int ret;
1516
1517 pthread_mutex_lock(&cache->lock);
1518 cachemap->flags &= ~flag;
1519 ret = cachemap->flags;
1520 pthread_mutex_unlock(&cache->lock);
1521
1522 return ret;
1523 }
1524
cache_check_header(CACHE * cache,CACHEMAP * cachemap)1525 void cache_check_header(CACHE * cache, CACHEMAP * cachemap)
1526 {
1527 int i, x;
1528 struct tm t;
1529 char **args;
1530 time_t utctime = utc_time(NULL);
1531
1532 cachemap->ftime = 0;
1533 cachemap->etime = 0;
1534 cachemap->lmtime = 0;
1535 cachemap->header->chunked = FALSE;
1536 cachemap->flags &= ~CACHE_EXPIRED;
1537
1538
1539 if (cachemap->header->expires != NULL) {
1540 putlog(MMLOG_DEBUG, "cache file expires header: %s", cachemap->header->expires);
1541
1542 memset(&t, 0, sizeof(t));
1543 strptime(cachemap->header->expires, HTTPTIMEFORMAT, &t);
1544 cachemap->etime = mktime(&t);
1545 }
1546
1547 if (cachemap->header->last_modified != NULL) {
1548 putlog(MMLOG_DEBUG, "cache file last-modified header: %s", cachemap->header->last_modified);
1549
1550 memset(&t, 0, sizeof(t));
1551 strptime(cachemap->header->last_modified, HTTPTIMEFORMAT, &t);
1552 cachemap->lmtime = mktime(&t);
1553 }
1554
1555 /* rfc says Cache-Control takes precedence over Expires: header */
1556 if (cachemap->header->cache_control != NULL) {
1557 putlog(MMLOG_DEBUG, "cache file cache-control header: %s", cachemap->header->cache_control);
1558
1559 args = string_break(cachemap->header->cache_control, ',');
1560
1561 for (x = 0; args[x]; x++) {
1562 if (!strncasecmp(args[x], "max-age=", 8)) {
1563 i = atoi(&args[x][8]);
1564
1565 cachemap->etime = cachemap->mtime + i;
1566 } else if (!strcasecmp(args[x], "must-revalidate"))
1567 cachemap->flags |= CACHE_EXPIRED;
1568 else if (!strcasecmp(args[x], "no-cache") || !strcasecmp(args[x], "no-store"))
1569 cachemap->flags |= (CACHE_EXPIRED | CACHE_VIOLATION);
1570
1571 xfree(args[x]);
1572 }
1573
1574 xfree(args);
1575 }
1576
1577 if (cachemap->lmtime != 0) {
1578 /* sanity check */
1579 if (cachemap->lmtime < cachemap->mtime) {
1580 /* calculate how long this file should be considered fresh based
1581 on a percentage of the delta between the time it was last modified
1582 and when it was downloaded or validated; this is used only
1583 if the server gives us nothing else to go on.
1584 */
1585 cachemap->ftime = cachemap->mtime + ((cachemap->mtime - cachemap->lmtime) * cache->lmfactor / 100);
1586
1587 putlog(MMLOG_DEBUG, "lmtime factor: %u", cachemap->ftime - cachemap->mtime);
1588 }
1589 }
1590
1591 if ((cachemap->etime == 0 && cachemap->ftime == 0) || (cachemap->mtime + cache->maxage < utctime) || (cachemap->etime != 0 && cachemap->etime < utctime) || (cachemap->ftime != 0 && cachemap->etime == 0 && cachemap->ftime < utctime) || (cachemap->lmtime != 0 && cachemap->lmtime + cache->minage > utctime) || (cachemap->etime == 0 && cachemap->mtime + cache->validate < utctime)) {
1592
1593 /* cache file has expired */
1594 cachemap->flags |= CACHE_EXPIRED;
1595 }
1596 }
1597
cache_validated(CACHE * cache,CACHEMAP * cachemap)1598 void cache_validated(CACHE * cache, CACHEMAP * cachemap)
1599 {
1600 pthread_mutex_lock(&cache->lock);
1601 cachemap->flags &= ~CACHE_VALIDATE;
1602 cache_touch_unlocked(cache, cachemap, TRUE);
1603 pthread_mutex_unlock(&cache->lock);
1604 }
1605
cache_invalidate_key(CACHE * cache,char * key)1606 int cache_invalidate_key(CACHE * cache, char *key)
1607 {
1608 int ret = FALSE;
1609 CACHEMAP *cachemap;
1610
1611 pthread_mutex_lock(&cache->lock);
1612
1613 cachemap = cache_hash_find(cache, key);
1614 if (cachemap != NULL) {
1615 cachemap->refcount++;
1616 cache_invalidate_unlocked(cache, cachemap);
1617 ret = TRUE;
1618 }
1619
1620 pthread_mutex_unlock(&cache->lock);
1621
1622 return ret;
1623 }
1624
cache_resize(CACHE * cache,CACHEMAP * cachemap,size_t size)1625 size_t cache_resize(CACHE * cache, CACHEMAP * cachemap, size_t size)
1626 {
1627 int x;
1628 void *ptr;
1629 size_t oldrs;
1630
1631 if (size == 0 || (cachemap->size + size > cachemap->realsize)) {
1632 /* this routine can block under some circumstances, releasing the mutex
1633 is safe. */
1634 pthread_mutex_unlock(&cache->lock);
1635
1636 oldrs = cachemap->realsize;
1637
1638 if (size == 0)
1639 cachemap->realsize = cachemap->size;
1640 else {
1641 /* if the content length is known, just map the whole thing now */
1642 if (cachemap->header->content_length != -1 && cachemap->header->content_length + cachemap->offset >= size)
1643 cachemap->realsize = cachemap->header->content_length + cachemap->offset;
1644 else {
1645 /* preallocation tends to hide some errors such as off-by-one's, so we won't do it if this is a debug
1646 build. */
1647 #ifdef _DEBUG
1648 cachemap->realsize = cachemap->size + size;
1649 #else
1650 cachemap->realsize = ALIGN2(cachemap->size + size, CACHE_ALIGNMENT);
1651 #endif
1652 }
1653 }
1654
1655 if (cachemap->fd != -1) {
1656 x = ftruncate(cachemap->fd, cachemap->realsize);
1657
1658 if (x == -1) {
1659 pthread_mutex_lock(&cache->lock);
1660 goto error;
1661 }
1662 }
1663
1664 if (!(cachemap->flags & CACHE_MEM))
1665 ptr = mmap(NULL, cachemap->realsize, PROT_WRITE | PROT_READ, (cachemap->fd != -1) ? MAP_SHARED : MAP_PRIVATE | MAP_ANON, cachemap->fd, 0);
1666 else
1667 ptr = p_mremap(cachemap->data, oldrs, cachemap->realsize, cachemap->fd);
1668
1669 pthread_mutex_lock(&cache->lock);
1670
1671 if (ptr == MAP_FAILED)
1672 goto error;
1673 else {
1674 if (!(cachemap->flags & CACHE_MEM)) {
1675 cache->mementries++;
1676 cachemap->flags |= CACHE_MEM;
1677 }
1678
1679 cachemap->data = ptr;
1680 }
1681 }
1682
1683 cachemap->size += size;
1684
1685 return cachemap->size;
1686
1687 error:
1688
1689 return 0;
1690 }
1691
cache_exists(CACHE * cache,char * key)1692 int cache_exists(CACHE * cache, char *key)
1693 {
1694 int ret = FALSE;
1695
1696 pthread_mutex_lock(&cache->lock);
1697 if (cache_hash_find(cache, key) != NULL)
1698 ret = TRUE;
1699 pthread_mutex_unlock(&cache->lock);
1700
1701 return ret;
1702 }
1703
cache_journal_write(CACHE * cache,struct cache_store * cstore)1704 void cache_journal_write(CACHE * cache, struct cache_store *cstore)
1705 {
1706 int i;
1707 char buf[256], buf2[256];
1708 FILE *fptr;
1709 CACHEMAP *cachemap;
1710 struct CACHEMAPLIST_T *cachemaplist;
1711
1712 snprintf(buf, sizeof(buf), "%s/journal.new", cstore->path);
1713 fptr = fopen(buf, "w+");
1714 if (fptr == NULL)
1715 return;
1716
1717 putlog(MMLOG_CACHE, "writing journal for %s", cstore->path);
1718
1719 for (i = 0; i < cache->hashsize; i++) {
1720 for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = cachemaplist->next) {
1721 cachemap = cachemaplist->cachemap;
1722
1723 if (cachemap->store != NULL && cachemap->store == cstore)
1724 fprintf(fptr, "%d\t%s\t%u\t%lu\t%lu\n", cachemap->flags, cachemap->key, cachemap->size, cachemap->mtime, cachemap->atime);
1725 }
1726 }
1727
1728 fclose(fptr);
1729
1730 snprintf(buf, sizeof(buf), "%s/journal.new", cstore->path);
1731 snprintf(buf2, sizeof(buf2), "%s/journal", cstore->path);
1732
1733 unlink(buf2);
1734 rename(buf, buf2);
1735
1736 return;
1737
1738 }
1739
cache_journal_read(CACHE * cache,struct cache_store * cstore)1740 void cache_journal_read(CACHE * cache, struct cache_store *cstore)
1741 {
1742 int i;
1743 char buf[8192], **args;
1744 size_t size;
1745 time_t mtime, atime;
1746 FILE *fptr;
1747 CACHEMAP *cachemap;
1748 struct CACHEMAPLIST_T *cachemaplist, *tmp;
1749
1750 snprintf(buf, sizeof(buf), "%s/journal", cstore->path);
1751 fptr = fopen(buf, "r");
1752 if (fptr == NULL) {
1753 cache_scandisk(cache, cstore);
1754 return;
1755 }
1756
1757 putlog(MMLOG_CACHE, "reading journal for %s", cstore->path);
1758
1759 /* read each journal entry and determine the last state a cache file was in */
1760 while (fgets(buf, sizeof(buf), fptr) != NULL) {
1761 args = string_break(buf, '\t');
1762 if (array_length(args) == 5) {
1763 size = strtoul(args[2], NULL, 10);
1764 mtime = strtoul(args[3], NULL, 10);
1765 atime = strtoul(args[4], NULL, 10);
1766
1767 cachemap = cache_hash_find(cache, args[1]);
1768 if (cachemap == NULL) {
1769 cachemap = cachemap_new(cache);
1770 cachemap->store = cstore;
1771 cachemap->key = xstrdup(args[1]);
1772 cache_hash_add(cache, cachemap);
1773 } else if (cachemap->refcount != 0)
1774 continue;
1775 else if (cachemap->store == NULL)
1776 cachemap->store = cstore;
1777
1778 /* we have to deal with the case when the same file shows up in more than one
1779 journal (i.e. it was invalidated in one cache store, then later created in another)...
1780 we'll use the newest copy */
1781 if (cachemap->store == cstore || (cachemap->mtime <= mtime && cachemap->store != cstore)) {
1782 cachemap->size = size;
1783 cachemap->flags = atoi(args[0]);
1784 cachemap->mtime = mtime;
1785 cachemap->atime = atime;
1786
1787 cachemap->store = cstore;
1788 }
1789 }
1790
1791 array_free(args);
1792 }
1793
1794 for (i = 0; i < cache->hashsize; i++) {
1795 for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = tmp) {
1796 tmp = cachemaplist->next;
1797 cachemap = cachemaplist->cachemap;
1798
1799 if (cachemap->store != cstore || cachemap->refcount != 0)
1800 continue;
1801
1802 /* these flags don't carry over between sessions */
1803 cachemap->flags &= ~CACHE_SESSION_FLAGS;
1804 /* XXX: ugly hack so redoing cache_journal_read on an active cache works */
1805 if (cachemap->data != NULL)
1806 cachemap->flags |= CACHE_MEM;
1807
1808 if ((cachemap->flags & (CACHE_WRITING | CACHE_INVALID)) || !(cachemap->flags & CACHE_DISK)) {
1809 /* the cache object's final state was invalid... remove it. */
1810
1811 cache_hash_remove(cache, cachemap);
1812
1813 if (cachemap->flags & CACHE_MEM)
1814 cache_mem_delete(cache, cachemap);
1815
1816 if (cachemap->flags & CACHE_DISK) {
1817 /* cache_disk_delete subtracts from the disksize
1818 accounting, but it hasn't been added yet */
1819 cachemap->store->disksize += cachemap->size;
1820 cachemap->store->diskentries++;
1821
1822 cache_disk_delete(cache, cachemap);
1823 }
1824 cache_free(cache, cachemap);
1825 } else {
1826 cachemap->store->diskentries++;
1827 cachemap->store->disksize += cachemap->size;
1828 }
1829 }
1830 }
1831
1832 fclose(fptr);
1833 }
1834
cache_journal_add(CACHE * cache,CACHEMAP * cachemap)1835 int cache_journal_add(CACHE * cache, CACHEMAP * cachemap)
1836 {
1837 int len;
1838 char buf[8192];
1839
1840 if (cachemap->store == NULL)
1841 return TRUE;
1842
1843 if (cachemap->store->journalfd == -1) {
1844 snprintf(buf, sizeof(buf), "%s/journal", cachemap->store->path);
1845 cachemap->store->journalfd = open(buf, O_WRONLY | O_CREAT | O_APPEND);
1846
1847 if (cachemap->store->journalfd == -1)
1848 return -1;
1849 }
1850
1851 len = snprintf(buf, sizeof(buf), "%d\t%s\t%u\t%lu\t%lu\n", cachemap->flags, cachemap->key, cachemap->size, cachemap->mtime, cachemap->atime);
1852
1853 write(cachemap->store->journalfd, buf, len);
1854
1855 cachemap->store->journalentries++;
1856
1857 if (cachemap->store->journalentries >= JOURNAL_SIZE) {
1858 /* rewrite the journal, this will remove redundant entries */
1859 close(cachemap->store->journalfd);
1860 cachemap->store->journalfd = -1;
1861
1862 cache_journal_write(cache, cachemap->store);
1863 cachemap->store->journalentries = 0;
1864 }
1865
1866 return TRUE;
1867 }
1868
1869
cachemap_new(CACHE * cache)1870 CACHEMAP *cachemap_new(CACHE * cache)
1871 {
1872 CACHEMAP *cachemap;
1873
1874 cachemap = xmalloc(sizeof(CACHEMAP));
1875 cachemap->flags = 0;
1876 cachemap->header = NULL;
1877 cachemap->fd = -1;
1878 cachemap->refcount = 0;
1879 cachemap->data = NULL;
1880 cachemap->store = NULL;
1881 pthread_cond_init(&cachemap->writecompletecond, NULL);
1882
1883 return cachemap;
1884 }
1885
unlink_list_add(char * file)1886 void unlink_list_add(char *file)
1887 {
1888 struct unlink_list *node;
1889
1890 node = xmalloc(sizeof(struct unlink_list));
1891 node->path = xstrdup(file);
1892
1893 node->next = NULL;
1894
1895 pthread_mutex_lock(&global->unlink_lock);
1896
1897 if (global->unlink_tail != NULL)
1898 global->unlink_tail->next = node;
1899 global->unlink_tail = node;
1900
1901 if (global->unlink_head == NULL)
1902 global->unlink_head = node;
1903
1904 pthread_cond_signal(&global->unlink_cond);
1905
1906 pthread_mutex_unlock(&global->unlink_lock);
1907 }
1908
1909
unlink_thread()1910 void unlink_thread()
1911 {
1912 int ret;
1913 struct unlink_list *tmp;
1914
1915 pthread_mutex_lock(&global->unlink_lock);
1916
1917 while (1) {
1918 if (global->unlink_head != NULL) {
1919 ret = unlink(global->unlink_head->path);
1920 if (ret == 0)
1921 putlog(MMLOG_DEBUG, "unlinked %s", global->unlink_head->path);
1922
1923 if (global->unlink_head == global->unlink_tail)
1924 global->unlink_tail = NULL;
1925
1926 tmp = global->unlink_head->next;
1927 xfree(global->unlink_head->path);
1928 xfree(global->unlink_head);
1929
1930 global->unlink_head = tmp;
1931
1932 /* ease up on disk activity and give other threads a chance to add
1933 items to the unlink list */
1934 pthread_mutex_unlock(&global->unlink_lock);
1935 usleep(10000);
1936 pthread_mutex_lock(&global->unlink_lock);
1937
1938 continue;
1939 }
1940
1941 pthread_cond_wait(&global->unlink_cond, &global->unlink_lock);
1942 }
1943 }
1944