📄 proxy_cache.c
字号:
/* ==================================================================== * Copyright (c) 1996-1998 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see <http://www.apache.org/>. * *//* Cache and garbage collection routines for Apache proxy */#include "mod_proxy.h"#include "http_log.h"#include "http_main.h"#include "util_date.h"#ifdef WIN32#include <sys/utime.h>#else#include <utime.h>#endif /* WIN32 */#include "multithread.h"#include "ap_md5.h"DEF_Explainstruct gc_ent { unsigned long int len; time_t expire; char file[HASH_LEN + 1];};/* Poor man's 61 bit arithmetic */typedef struct { long lower; /* lower 30 bits of result */ long upper; /* upper 31 bits of result */} long61_t;/* FIXME: The block size can be different on a `per file system' base. * This would make automatic detection highly OS specific. * In the GNU fileutils code for du(1), you can see how complicated it can * become to detect the block size. And, with BSD-4.x fragments, it * it even more difficult to get precise results. * As a compromise (and to improve on the incorrect counting of cache * size on byte level, omitting directory sizes entirely, which was * used up to apache-1.3b7) we're rounding to multiples of 512 here. * Your file system may be using larger blocks (I certainly hope so!) * but it will hardly use smaller blocks. * (So this approximation is still closer to reality than the old behavior). * The best solution would be automatic detection, the next best solution * IMHO is a sensible default and the possibility to override it. */#define ROUNDUP2BLOCKS(_bytes) (((_bytes)+block_size-1) & ~(block_size-1))static long block_size = 512; /* this must be a power of 2 */static long61_t curbytes, cachesize;static time_t every, garbage_now, garbage_expire;static char *filename;static mutex *garbage_mutex = NULL;int ap_proxy_garbage_init(server_rec *r, pool *p){ if (!garbage_mutex) garbage_mutex = ap_create_mutex(NULL); return (0);}static int sub_garbage_coll(request_rec *r, array_header *files, const char *cachedir, const char *cachesubdir);static void help_proxy_garbage_coll(request_rec *r);#if !defined(WIN32) && !defined(MPE) && !defined(OS2)static void detached_proxy_garbage_coll(request_rec *r);#endifvoid ap_proxy_garbage_coll(request_rec *r){ static int inside = 0; (void) ap_acquire_mutex(garbage_mutex); if (inside == 1) { (void) ap_release_mutex(garbage_mutex); return; } else inside = 1; (void) ap_release_mutex(garbage_mutex); ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */#if !defined(WIN32) && !defined(MPE) && !defined(OS2) detached_proxy_garbage_coll(r);#else help_proxy_garbage_coll(r);#endif ap_unblock_alarms(); (void) ap_acquire_mutex(garbage_mutex); inside = 0; (void) ap_release_mutex(garbage_mutex);}static voidadd_long61 (long61_t *accu, long val){ /* Add in lower 30 bits */ accu->lower += (val & 0x3FFFFFFFL); /* add in upper bits, and carry */ accu->upper += (val >> 30) + ((accu->lower & ~0x3FFFFFFFL) != 0L); /* Clear carry */ accu->lower &= 0x3FFFFFFFL;}static voidsub_long61 (long61_t *accu, long val){ int carry = (val & 0x3FFFFFFFL) > accu->lower; /* Subtract lower 30 bits */ accu->lower = accu->lower - (val & 0x3FFFFFFFL) + ((carry) ? 0x40000000 : 0); /* add in upper bits, and carry */ accu->upper -= (val >> 30) + carry;}/* Compare two long61's: * return <0 when left < right * return 0 when left == right * return >0 when left > right */static longcmp_long61 (long61_t *left, long61_t *right){ return (left->upper == right->upper) ? (left->lower - right->lower) : (left->upper - right->upper);}/* Compare two gc_ent's, sort them by expiration date */static int gcdiff(const void *ap, const void *bp){ const struct gc_ent *a = (const struct gc_ent * const) ap; const struct gc_ent *b = (const struct gc_ent * const) bp; if (a->expire > b->expire) return 1; else if (a->expire < b->expire) return -1; else return 0;}#if !defined(WIN32) && !defined(MPE) && !defined(OS2)static void detached_proxy_garbage_coll(request_rec *r){ pid_t pid; int status; pid_t pgrp; switch (pid = fork()) { case -1: ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: fork() for cache cleanup failed"); return; case 0: /* Child */ /* close all sorts of things, including the socket fd */ ap_cleanup_for_exec(); /* Fork twice to disassociate from the child */ switch (pid = fork()) { case -1: ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: fork(2nd) for cache cleanup failed"); exit(1); case 0: /* Child */ /* The setpgrp() stuff was snarfed from http_main.c */#ifndef NO_SETSID if ((pgrp = setsid()) == -1) { perror("setsid"); fprintf(stderr, "httpd: setsid failed\n"); exit(1); }#elif defined(NEXT) || defined(NEWSOS) if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { perror("setpgrp"); fprintf(stderr, "httpd: setpgrp or getpgrp failed\n"); exit(1); }#else if ((pgrp = setpgrp(getpid(), 0)) == -1) { perror("setpgrp"); fprintf(stderr, "httpd: setpgrp failed\n"); exit(1); }#endif help_proxy_garbage_coll(r); exit(0); default: /* Father */ /* After grandson has been forked off, */ /* there's nothing else to do. */ exit(0); } default: /* Wait until grandson has been forked off */ /* (without wait we'd leave a zombie) */ waitpid(pid, &status, 0); return; }}#endif /* ndef WIN32 */static void help_proxy_garbage_coll(request_rec *r){ const char *cachedir; void *sconf = r->server->module_config; proxy_server_conf *pconf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); const struct cache_conf *conf = &pconf->cache; array_header *files; struct stat buf; struct gc_ent *fent; int i, timefd; static time_t lastcheck = BAD_DATE; /* static (per-process) data!!! */ cachedir = conf->root; /* configured size is given in kB. Make it bytes, convert to long61_t: */ cachesize.lower = cachesize.upper = 0; add_long61(&cachesize, conf->space << 10); every = conf->gcinterval; if (cachedir == NULL || every == -1) return; garbage_now = time(NULL); /* Usually, the modification time of <cachedir>/.time can only increase. * Thus, even with several child processes having their own copy of * lastcheck, if time(NULL) still < lastcheck then it's not time * for GC yet. */ if (garbage_now != -1 && lastcheck != BAD_DATE && garbage_now < lastcheck + every) return; ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2); strcpy(filename, cachedir); strcat(filename, "/.time"); if (stat(filename, &buf) == -1) { /* does not exist */ if (errno != ENOENT) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: stat(%s)", filename); ap_unblock_alarms(); return; } if ((timefd = creat(filename, 0666)) == -1) { if (errno != EEXIST) ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: creat(%s)", filename); else lastcheck = garbage_now; /* someone else got in there */ ap_unblock_alarms(); return; } close(timefd); } else { lastcheck = buf.st_mtime; /* save the time */ if (garbage_now < lastcheck + every) { ap_unblock_alarms(); return; } if (utime(filename, NULL) == -1) ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: utimes(%s)", filename); } files = ap_make_array(r->pool, 100, sizeof(struct gc_ent)); curbytes.upper = curbytes.lower = 0L; sub_garbage_coll(r, files, cachedir, "/"); if (cmp_long61(&curbytes, &cachesize) < 0L) { ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "proxy GC: Cache is %ld%% full (nothing deleted)", (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space)); ap_unblock_alarms(); return; } /* sort the files we found by expiration date */ qsort(files->elts, files->nelts, sizeof(struct gc_ent), gcdiff); for (i = 0; i < files->nelts; i++) { fent = &((struct gc_ent *) files->elts)[i]; sprintf(filename, "%s%s", cachedir, fent->file); Explain3("GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, fent->expire, garbage_now);#if TESTING fprintf(stderr, "Would unlink %s\n", filename);#else if (unlink(filename) == -1) { if (errno != ENOENT) ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy gc: unlink(%s)", filename); } else#endif { sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len)); if (cmp_long61(&curbytes, &cachesize) < 0) break; } } ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "proxy GC: Cache is %ld%% full (%d deleted)", (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i); ap_unblock_alarms();}static int sub_garbage_coll(request_rec *r, array_header *files, const char *cachebasedir, const char *cachesubdir){ char line[27]; char cachedir[HUGE_STRING_LEN]; struct stat buf; int fd, i; DIR *dir;#if defined(NEXT) || defined(WIN32) struct DIR_TYPE *ent;#else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -