/* * mod_lock: implements a simple method for conditional lock. * useful for locking a single document tree or a virtual host without * affecting other servers. * * Lyonel VINCENT * * usage: * SetLockFile * default value: none * context: Directory * effect: Enables the conditional lock for a location. * The web server will check for the existence of * the file each time it will access the concerned * documents. If it finds it, it will return a * 503 (Service unavailable) HTTP status code. * remarks: if is a null string (""), conditional * lock will be disabled for this class of * documents. * * LockBypass ... * default value: none * context: Directory * effect: Allow access from an host, even if locked. * remarks: hostnames can be either real hostnames * (www.hp.com) or domain names (.hp.com). * * example: * * SetLockFile /www/locks/root * LockBypass support.hp.com .grenoble.hp.com * ErrorDocument 503 /maintenance.html * * */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" typedef struct { char * hostname; } bypass; typedef struct { char * lockfile; array_header * bypass; } lock_config_rec; module lock_module; static const char *lock_set_lockfile (cmd_parms *cmd, lock_config_rec *sec, char * arg) { sec->lockfile=arg; return NULL; } static const char *lock_set_bypass (cmd_parms *cmd, lock_config_rec *sec, char * arg) { bypass * host; host = (bypass *)ap_push_array(sec->bypass); host->hostname = ap_pstrdup(cmd->pool, arg); return NULL; } static command_rec lock_cmds[] = { { "SetLockFile", lock_set_lockfile, NULL, OR_AUTHCFG, TAKE1, "Filename" }, { "LockBypass", lock_set_bypass, NULL, OR_AUTHCFG, ITERATE, "Hostname" }, { NULL } }; static void *create_lock_dir_config (pool *p, char *d) { lock_config_rec * sec = (lock_config_rec *)ap_pcalloc (p, sizeof(lock_config_rec)); if (!sec) return NULL; /* no memory... */ sec -> lockfile = ""; sec -> bypass = ap_make_array (p, 1, sizeof (bypass)); return sec; } static void *merge_lock_dir_config (pool *p, void *pdir, void *sdir) { lock_config_rec *parent_dir = (lock_config_rec *)pdir, *subdir = (lock_config_rec *)sdir; lock_config_rec *new = (lock_config_rec *)ap_palloc (p, sizeof(lock_config_rec)); /* if a directory does not have a lock file, use the lock file of its parent */ if(strlen(subdir->lockfile)) new->lockfile = ap_pstrdup(p,subdir->lockfile); else new->lockfile = ap_pstrdup(p,parent_dir->lockfile); new->bypass = ap_append_arrays(p, subdir->bypass, parent_dir->bypass); return (void*)new; } static int in_domain(const char *domain, const char *what) { int dl=strlen(domain); int wl=strlen(what); if((wl-dl) >= 0) { if (strcasecmp(domain,&what[wl-dl]) != 0) return 0; /* Make sure we matched an *entire* subdomain --- if the user * said 'allow from good.com', we don't want people from nogood.com * to be able to get in. */ if (wl == dl) return 1; /* matched whole thing */ else return (domain[0] == '.' || what[wl - dl - 1] == '.'); } else return 0; } static int lock_handler(request_rec *r) { lock_config_rec *sec = (lock_config_rec *)ap_get_module_config(r->per_dir_config, &lock_module); FILE * f = NULL; const char * remotehost = NULL; bypass * hosts = (bypass*)sec->bypass->elts; int i = 0; if(!strlen(sec->lockfile) || /* conditional lock disabled */ (r->prev && /* redirected through ErrorDocument */ (r->prev->status==HTTP_SERVICE_UNAVAILABLE)) ) return DECLINED; if(!(f=ap_pfopen(r->pool,sec->lockfile,"r"))) switch(errno) { case EACCES: break; /* access denied */ case EISDIR: break; /* the file is a directory */ default: return DECLINED; /* lock file does not exist */ } if(f) ap_pfclose(r->pool,f); /* now we check if the remote host is allowed to bypass the lock */ remotehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST); if(remotehost) for(i=0; i < sec->bypass->nelts; i++) if(in_domain(hosts[i].hostname, remotehost)) return DECLINED; /* access granted */ return HTTP_SERVICE_UNAVAILABLE; /* sorry */ } static handler_rec lock_handlers[] = { { "*/*", lock_handler }, { NULL } }; module lock_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ create_lock_dir_config, /* dir config creater */ merge_lock_dir_config, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ lock_cmds, /* command table */ lock_handlers, /* handlers */ NULL, /* filename translation */ NULL, /* check_user_id */ NULL, /* check access rights */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL /* header parser */ };