📄 mod_ext_filter.c
字号:
/* Copyright 2002-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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. *//* * mod_ext_filter allows Unix-style filters to filter http content. */#include "httpd.h"#include "http_config.h"#include "http_log.h"#include "http_protocol.h"#define CORE_PRIVATE#include "http_core.h"#include "apr_buckets.h"#include "util_filter.h"#include "util_script.h"#include "util_time.h"#include "apr_strings.h"#include "apr_hash.h"#include "apr_lib.h"#include "apr_poll.h"#define APR_WANT_STRFUNC#include "apr_want.h"typedef struct ef_server_t { apr_pool_t *p; apr_hash_t *h;} ef_server_t;typedef struct ef_filter_t { const char *name; enum {INPUT_FILTER=1, OUTPUT_FILTER} mode; ap_filter_type ftype; const char *command; const char *enable_env; const char *disable_env; char **args; const char *intype; /* list of IMTs we process (well, just one for now) */#define INTYPE_ALL (char *)1 const char *outtype; /* IMT of filtered output */#define OUTTYPE_UNCHANGED (char *)1 int preserves_content_length;} ef_filter_t;typedef struct ef_dir_t { int debug; int log_stderr;} ef_dir_t;typedef struct ef_ctx_t { apr_pool_t *p; apr_proc_t *proc; apr_procattr_t *procattr; ef_dir_t *dc; ef_filter_t *filter; int noop;#if APR_FILES_AS_SOCKETS apr_pollfd_t *pollset;#endif} ef_ctx_t;module AP_MODULE_DECLARE_DATA ext_filter_module;static const server_rec *main_server;static apr_status_t ef_output_filter(ap_filter_t *, apr_bucket_brigade *);#define DBGLVL_SHOWOPTIONS 1#define DBGLVL_ERRORCHECK 2#define DBGLVL_GORY 9#define ERRFN_USERDATA_KEY "EXTFILTCHILDERRFN"static void *create_ef_dir_conf(apr_pool_t *p, char *dummy){ ef_dir_t *dc = (ef_dir_t *)apr_pcalloc(p, sizeof(ef_dir_t)); dc->debug = -1; dc->log_stderr = -1; return dc;}static void *create_ef_server_conf(apr_pool_t *p, server_rec *s){ ef_server_t *conf; conf = (ef_server_t *)apr_pcalloc(p, sizeof(ef_server_t)); conf->p = p; conf->h = apr_hash_make(conf->p); return conf;}static void *merge_ef_dir_conf(apr_pool_t *p, void *basev, void *overridesv){ ef_dir_t *a = (ef_dir_t *)apr_pcalloc (p, sizeof(ef_dir_t)); ef_dir_t *base = (ef_dir_t *)basev, *over = (ef_dir_t *)overridesv; if (over->debug != -1) { /* if admin coded something... */ a->debug = over->debug; } else { a->debug = base->debug; } if (over->log_stderr != -1) { /* if admin coded something... */ a->log_stderr = over->log_stderr; } else { a->log_stderr = base->log_stderr; } return a;}static const char *add_options(cmd_parms *cmd, void *in_dc, const char *arg){ ef_dir_t *dc = in_dc; if (!strncasecmp(arg, "DebugLevel=", 11)) { dc->debug = atoi(arg + 11); } else if (!strcasecmp(arg, "LogStderr")) { dc->log_stderr = 1; } else if (!strcasecmp(arg, "NoLogStderr")) { dc->log_stderr = 0; } else { return apr_pstrcat(cmd->temp_pool, "Invalid ExtFilterOptions option: ", arg, NULL); } return NULL;}static const char *parse_cmd(apr_pool_t *p, const char **args, ef_filter_t *filter){ if (**args == '"') { const char *start = *args + 1; char *parms; int escaping = 0; apr_status_t rv; ++*args; /* move past leading " */ /* find true end of args string (accounting for escaped quotes) */ while (**args && (**args != '"' || (**args == '"' && escaping))) { if (escaping) { escaping = 0; } else if (**args == '\\') { escaping = 1; } ++*args; } if (**args != '"') { return "Expected cmd= delimiter"; } /* copy *just* the arg string for parsing, */ parms = apr_pstrndup(p, start, *args - start); ++*args; /* move past trailing " */ /* parse and tokenize the args. */ rv = apr_tokenize_to_argv(parms, &(filter->args), p); if (rv != APR_SUCCESS) { return "cmd= parse error"; } } else { /* simple path */ /* Allocate space for two argv pointers and parse the args. */ filter->args = (char **)apr_palloc(p, 2 * sizeof(char *)); filter->args[0] = ap_getword_white(p, args); filter->args[1] = NULL; /* end of args */ } if (!filter->args[0]) { return "Invalid cmd= parameter"; } filter->command = filter->args[0]; return NULL;}static const char *define_filter(cmd_parms *cmd, void *dummy, const char *args){ ef_server_t *conf = ap_get_module_config(cmd->server->module_config, &ext_filter_module); const char *token; const char *name; ef_filter_t *filter; name = ap_getword_white(cmd->pool, &args); if (!name) { return "Filter name not found"; } if (apr_hash_get(conf->h, name, APR_HASH_KEY_STRING)) { return apr_psprintf(cmd->pool, "ExtFilter %s is already defined", name); } filter = (ef_filter_t *)apr_pcalloc(conf->p, sizeof(ef_filter_t)); filter->name = name; filter->mode = OUTPUT_FILTER; filter->ftype = AP_FTYPE_RESOURCE; apr_hash_set(conf->h, name, APR_HASH_KEY_STRING, filter); while (*args) { while (apr_isspace(*args)) { ++args; } /* Nasty parsing... I wish I could simply use ap_getword_white() * here and then look at the token, but ap_getword_white() doesn't * do the right thing when we have cmd="word word word" */ if (!strncasecmp(args, "preservescontentlength", 22)) { token = ap_getword_white(cmd->pool, &args); if (!strcasecmp(token, "preservescontentlength")) { filter->preserves_content_length = 1; } else { return apr_psprintf(cmd->pool, "mangled argument `%s'", token); } continue; } if (!strncasecmp(args, "mode=", 5)) { args += 5; token = ap_getword_white(cmd->pool, &args); if (!strcasecmp(token, "output")) { filter->mode = OUTPUT_FILTER; } else if (!strcasecmp(token, "input")) { filter->mode = INPUT_FILTER; } else { return apr_psprintf(cmd->pool, "Invalid mode: `%s'", token); } continue; } if (!strncasecmp(args, "ftype=", 6)) { args += 6; token = ap_getword_white(cmd->pool, &args); filter->ftype = atoi(token); continue; } if (!strncasecmp(args, "enableenv=", 10)) { args += 10; token = ap_getword_white(cmd->pool, &args); filter->enable_env = token; continue; } if (!strncasecmp(args, "disableenv=", 11)) { args += 11; token = ap_getword_white(cmd->pool, &args); filter->disable_env = token; continue; } if (!strncasecmp(args, "intype=", 7)) { args += 7; filter->intype = ap_getword_white(cmd->pool, &args); continue; } if (!strncasecmp(args, "outtype=", 8)) { args += 8; filter->outtype = ap_getword_white(cmd->pool, &args); continue; } if (!strncasecmp(args, "cmd=", 4)) { args += 4; if ((token = parse_cmd(cmd->pool, &args, filter))) { return token; } continue; } return apr_psprintf(cmd->pool, "Unexpected parameter: `%s'", args); } /* parsing is done... register the filter */ if (filter->mode == OUTPUT_FILTER) { /* XXX need a way to ensure uniqueness among all filters */ ap_register_output_filter(filter->name, ef_output_filter, NULL, filter->ftype); }#if 0 /* no input filters yet */ else if (filter->mode == INPUT_FILTER) { /* XXX need a way to ensure uniqueness among all filters */ ap_register_input_filter(filter->name, ef_input_filter, NULL, AP_FTYPE_RESOURCE); }#endif else { ap_assert(1 != 1); /* we set the field wrong somehow */ } return NULL;}static const command_rec cmds[] ={ AP_INIT_ITERATE("ExtFilterOptions", add_options, NULL, ACCESS_CONF, /* same as SetInputFilter/SetOutputFilter */ "valid options: DebugLevel=n, LogStderr, NoLogStderr"), AP_INIT_RAW_ARGS("ExtFilterDefine", define_filter, NULL, RSRC_CONF, "Define an external filter"), {NULL}};static int ef_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_s){ main_server = main_s; return OK;}static void register_hooks(apr_pool_t *p){ ap_hook_post_config(ef_init, NULL, NULL, APR_HOOK_MIDDLE);}static apr_status_t set_resource_limits(request_rec *r, apr_procattr_t *procattr){#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) core_dir_config *conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); apr_status_t rv;#ifdef RLIMIT_CPU rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, conf->limit_cpu); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */#endif#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, conf->limit_mem); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */#endif#ifdef RLIMIT_NPROC rv = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, conf->limit_nproc); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */#endif#endif /* if at least one limit defined */ return APR_SUCCESS;}static apr_status_t ef_close_file(void *vfile){ return apr_file_close(vfile);}static void child_errfn(apr_pool_t *pool, apr_status_t err, const char *description){ request_rec *r; void *vr; apr_file_t *stderr_log; char errbuf[200]; char time_str[APR_CTIME_LEN]; apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); r = vr; apr_file_open_stderr(&stderr_log, pool); ap_recent_ctime(time_str, apr_time_now()); apr_file_printf(stderr_log, "[%s] [client %s] mod_ext_filter (%d)%s: %s\n", time_str, r->connection->remote_ip, err, apr_strerror(err, errbuf, sizeof(errbuf)), description);}/* init_ext_filter_process: get the external filter process going * This is per-filter-instance (i.e., per-request) initialization. */static apr_status_t init_ext_filter_process(ap_filter_t *f){ ef_ctx_t *ctx = f->ctx; apr_status_t rc; ef_dir_t *dc = ctx->dc; const char * const *env; ctx->proc = apr_pcalloc(ctx->p, sizeof(*ctx->proc)); rc = apr_procattr_create(&ctx->procattr, ctx->p); ap_assert(rc == APR_SUCCESS); rc = apr_procattr_io_set(ctx->procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK); ap_assert(rc == APR_SUCCESS); rc = set_resource_limits(f->r, ctx->procattr); ap_assert(rc == APR_SUCCESS); if (dc->log_stderr > 0) { rc = apr_procattr_child_err_set(ctx->procattr, f->r->server->error_log, /* stderr in child */ NULL); ap_assert(rc == APR_SUCCESS); } rc = apr_procattr_child_errfn_set(ctx->procattr, child_errfn); ap_assert(rc == APR_SUCCESS); apr_pool_userdata_set(f->r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ctx->p); if (dc->debug >= DBGLVL_ERRORCHECK) { rc = apr_procattr_error_check_set(ctx->procattr, 1); ap_assert(rc == APR_SUCCESS); } /* add standard CGI variables as well as DOCUMENT_URI, DOCUMENT_PATH_INFO,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -