📄 proxy_cache.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* Cache and garbage collection routines for Apache proxy */#include "mod_proxy.h"#include "http_conf_globals.h"#include "http_log.h"#include "http_main.h"#include "http_core.h"#include "util_date.h"#ifdef WIN32#include <sys/utime.h>#else#include <utime.h>#endif /* WIN32 */#include "multithread.h"#include "ap_md5.h"#ifdef __TANDEM#include <sys/types.h>#include <sys/stat.h>#endif#ifdef TPF#include "os.h"#endifstruct 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 garbage_now, garbage_expire;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);static int should_proxy_garbage_coll(request_rec *r);#if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)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 (should_proxy_garbage_coll(r))#if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF) 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 void add_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 void sub_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 long cmp_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 *) ap; const struct gc_ent *b = (const struct gc_ent *) 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) && !defined(NETWARE) && !defined(TPF)static void detached_proxy_garbage_coll(request_rec *r){ pid_t pid; int status; pid_t pgrp;#if 0 ap_log_error(APLOG_MARK, APLOG_DEBUG, r->server, "proxy: Guess what; we fork() again...");#endif 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, "%s: setsid failed\n", ap_server_argv0); exit(1); }#elif defined(NEXT) || defined(NEWSOS) if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { perror("setpgrp"); fprintf(stderr, "%S: setpgrp or getpgrp failed\n", ap_server_argv0); exit(1); }#elif defined(CYGWIN) /* Cygwin does not take any argument for setpgrp() */ if ((pgrp = setpgrp()) == -1) { perror("setpgrp"); fprintf(stderr, "%S: setpgrp failed\n", ap_server_argv0); exit(1); }#else if ((pgrp = setpgrp(getpid(), 0)) == -1) { perror("setpgrp"); fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0); 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 */#define DOT_TIME "/.time" /* marker */static int should_proxy_garbage_coll(request_rec *r){ 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; const char *cachedir = conf->root; char *filename; struct stat buf; int timefd; time_t every = conf->gcinterval; static time_t lastcheck = BAD_DATE; /* static (per-process) data!!! */ if (cachedir == NULL || every == -1) return 0; filename = ap_palloc(r->pool, strlen(cachedir) + strlen(DOT_TIME) + 1); 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 0; strcpy(filename, cachedir); strcat(filename, DOT_TIME); /* * At this point we have a bit of an engineering compromise. We could * either create and/or mark the .time file (prior to the fork which * might fail on a resource issue) or wait until we are safely forked. * The advantage of doing it now in this process is that we get some * usefull live out of the global last check variable. (XXX which should * go scoreboard IMHO.) Note that the actual counting is at a later * moment. */ if (stat(filename, &buf) == -1) { /* does not exist */ if (errno != ENOENT) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: stat(%s)", filename); return 0; } 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 */ return 0; } close(timefd); } else { lastcheck = buf.st_mtime; /* save the time */ if (garbage_now < lastcheck + every) { return 0; } if (utime(filename, NULL) == -1) ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "proxy: utimes(%s)", filename); } return 1;}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 gc_ent *fent; char *filename; int i; cachedir = conf->root; filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2); /* configured size is given in kB. Make it bytes, convert to long61_t: */ cachesize.lower = cachesize.upper = 0; add_long61(&cachesize, conf->space << 10); ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ 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); ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now);#if TESTING
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -