[EMAIL PROTECTED]
i just developed a simple input filter which seems to cause segfaults after
about an hour of usage (apache v2.0.49)
what happens:
after apache restart everything works fine, but after about an hour i get
segfaults on every request
what i need:
maybe someone has a few minutes and have a look at my module source in hope an
expirienced developer spot's a bug
the the code should do:
i just want a transparent apache module which waits for a request of specific
url which is configurable
i use a inputfilter, so it isn't necessary that the file really exists
in case the url equals the module configuration i write the header/post and get
data in a file
afaik there are other modules which do similar stuff, but i just wanted to try
it and i need a easy changeable file format (= my own)
what could be the problem:
i think of 3 possibilities
1. my brigade/bucket iteration for post data doesn't work correct
2. the module isn't really transparent (maybe the filter return values are a
problem OR ap_get_brigade causes a problem in my handle_modulerequest function)
3. i have some very basic and trivial C bug (in this case, excuse for this mail
^^)
[EMAIL PROTECTED] for your time
the code:
#include "apr_strings.h"
#include "apr_user.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <strings.h>
#include <time.h>
#include <sys/file.h>
#define APR_WANT_STRFUNC
#include "apr_want.h"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#define LOGLINE() fprintf(stderr, "%s:%d\n",__FILE__,__LINE__); fflush(stderr)
#define LOG(...) fprintf(stderr, "%s:%d ",__FILE__,__LINE__);
fprintf(stderr,__VA_ARGS__); fflush(stderr)
// gcc -Wall -fPIC -c -I /usr/include/apache2 mod_mymodule.c
// gcc -shared -Wl,-soname,mod_mymodule.so.1 -o mod_mymodule.so mod_mymodule.o
#define TRDOMAIN 1
#define TRPATH 2
#define TRENABLE 3
#define TRLOCALPATH 4
static const char *mymoduleFilterName = "MYMODULE";
#define LOCALPATH "/tmp/mod_mymodule"
static int tr_domain = TRDOMAIN;
static int tr_path = TRPATH;
static int tr_enable = TRENABLE;
static int tr_localpath = TRLOCALPATH;
#ifndef MAX_PATH
#define MAX_PATH 1024
#endif
typedef struct {
int enabled;
char *moduledomain;
char *modulepath;
char *localpath;
} module_rec;
module AP_MODULE_DECLARE_DATA mymodule_module;
/* create a directory recursively */
static int recursivemkdir(char *path){
if(path==NULL){ return 0; } // if path is null, return error
struct stat sb;
if(stat(path,&sb)==0){ return 1; } // if path exists, all ok
if(mkdir(path,0775)==0){ return 1; } // if mkdir doesnt fail, all ok
if(errno!=ENOENT){ return 0; } // if wrong errno, return error
char *pos = strrchr(path,'/');
if(pos==NULL){
return 0;
}
*pos = '\0'; // try to create the most top level directory
if(recursivemkdir(path)==0) return 0; // failed, just return 0
*pos = '/'; // if mkdir succeeded, try create the full path
if(recursivemkdir(path)==0) return 0; // failed, just return 0
return 1; // succeed!! directory created
}
/* write header data into file */
static int header_iterator(void *h,const char *key,const char *value){
if(h==NULL){ return 1; }
FILE *fp = (FILE*)h;
fprintf(fp,"%s %s\n",key,value);
return 1;
}
/* checks if request equals configuration and writes header,post,get as XML in
file*/
static apr_status_t handle_modulerequest(ap_filter_t *f,
apr_bucket_brigade *bb,
ap_input_mode_t mode,
apr_read_type_e block,
apr_off_t readbytes)
{
const char *str;
apr_size_t length;
apr_status_t finished = ap_get_brigade(f->next, bb, mode, block,
readbytes); // don't know, just copied from another module :)
#define RETFINISHED() { ap_remove_input_filter(f); return finished; } // i
have no idea if this is ok!?
if(finished!=APR_SUCCESS) RETFINISHED(); // copied
if(!f) RETFINISHED(); // just in case
request_rec *r = f->r;
if(!r || r->main) RETFINISHED(); // just in case
module_rec *tcfg = ap_get_module_config(r->per_dir_config,
&mymodule_module); // get configuration
if(!tcfg) RETFINISHED(); // just in case
char *args="",*path_info="",*unparsed_uri="",*uri="";
if(r->args){ args = r->args; }
if(r->path_info){ path_info = r->path_info; }
if(r->unparsed_uri){ unparsed_uri = r->unparsed_uri; }
if(r->uri){ uri = r->uri; }
const char *host = apr_table_get(r->headers_in, "Host");
if(args && uri && host && tcfg->modulepath && tcfg->moduledomain &&
tcfg->localpath && strstr(args,"mymoduletest=doit")){
LOG("MYMODULE: this is a test request{IST,SOLL}, ModulePath
{'%s','%s'}, ModuleDomain {'%s','%s'}, GET {'%s'}, enabled: '%d', localpath
{'%s'}\n",
uri,tcfg->modulepath,host,tcfg->moduledomain,args,tcfg->enabled,tcfg->localpath);
RETFINISHED();
}
// if not enabled or options set
if(tcfg->enabled!=1 || !tcfg->moduledomain || !tcfg->modulepath)
RETFINISHED();
// if the request equals configuration
if(uri && host && tcfg->modulepath && tcfg->moduledomain &&
strcasecmp(host,tcfg->moduledomain)==0 && strcasecmp(uri,tcfg->modulepath)==0){
char dafilename[1025];
memset(dafilename,0,sizeof(dafilename));
if(args!=NULL && strlen(args)>0){
char *begin = strstr(args,"dafilename"); // get file name from http
get
if(begin!=NULL){
begin = strchr(begin,'=');
char *end = strchr(begin,'&');
if(begin!=NULL){
begin++;
int len = sizeof(dafilename)-1;
if(end!=NULL){
int len =
sizeof(dafilename)-1<end-begin?sizeof(dafilename)-1:end-begin;
strncpy(dafilename,begin,len);
}
else{
strncpy(dafilename,begin,len);
}
}
}
}
// just creating path to directory
if(strlen(dafilename)<=0){
snprintf(dafilename,sizeof(dafilename)-1,"default");
}
char file[MAX_PATH];
time_t tnow = time(NULL);
struct tm now;
localtime_r(&tnow,&now);
strftime(file,sizeof(file)-1,tcfg->localpath,&now);
// recursively create directory if necessary
if(recursivemkdir(file)==0){
char curdir[1024];
memset(curdir,0,sizeof(curdir)-1);
getcwd(curdir,sizeof(curdir)-1);
LOG("MYMODULE: Couldn't create mymodule directory (%s, curdir %s),
disabling module, %d:%s\n",tcfg->localpath,curdir,errno,strerror(errno));
tcfg->enabled = 0;
RETFINISHED();
}
// create tmp file
strcat(file,"/");
char tmpfile[MAX_PATH];
snprintf(tmpfile,sizeof(tmpfile)-1,"%s%s.XXXXXX",file,dafilename);
strcat(file,dafilename);
int fd = mkstemp(tmpfile);
if(fd!=-1){
// open temporary xml file for writing
FILE *fp = fdopen(fd,"a+");
fprintf(fp,"<request>\n");
fprintf(fp,"<date>%lu</date>\n",time(NULL));
fprintf(fp,"<header>\n");
// write header to file, does (void*)fp really work in every
circumstances?
apr_table_do(header_iterator,(void*)fp,r->headers_in,NULL);
fprintf(fp,"</header>\n");
fprintf(fp,"<param %s>",args);
apr_bucket *bkt = NULL;
// iterate through post data
if(APR_BRIGADE_EMPTY(bb)) RETFINISHED();
for(bkt=APR_BRIGADE_FIRST(bb);bkt!=APR_BRIGADE_SENTINEL(bb);bkt=APR_BUCKET_NEXT(bkt)){
if (APR_BUCKET_IS_EOS(bkt)){ break; }
if(apr_bucket_read(bkt, &str, &length, APR_BLOCK_READ)!=APR_SUCCESS
|| length<=0 || str==NULL){
continue;
}
fwrite(str,sizeof(char),length,fp);
}
// do i have to delete or remove some bucket stuff here?
fprintf(fp,"</param>\n");
fprintf(fp,"</request>\n\n");
LOGLINE();
rewind(fp);
char buffer[1024];
int len = 0;
// open destination file and append everything from temporary file
FILE *dest = fopen(file,"a+");
if(dest!=NULL){
flock(fileno(dest),LOCK_EX);
fseek(dest,0,SEEK_END);
while(!feof(fp)){
if((len=fread(buffer,sizeof(char),sizeof(buffer),fp))){
fwrite(buffer,sizeof(char),len,dest);
}
}
flock(fileno(dest),LOCK_UN);
fclose(dest);
}
fclose(fp);
unlink(tmpfile);
}
return APR_EGENERAL;
}
RETFINISHED();
#undef RETFINISHED
}
// creating module configuration
static void* make_module_dir(apr_pool_t *p,char *d){
module_rec *tcfg;
if(!p){ return NULL; }
tcfg = (module_rec *) apr_pcalloc(p, sizeof(module_rec));
if(!tcfg){ return NULL; }
tcfg->moduledomain = NULL;
tcfg->modulepath = NULL;
tcfg->enabled = 2; // waiting for initializiation
tcfg->localpath = LOCALPATH;
return tcfg;
}
// set module configuration strings
static const char* set_module_strings(cmd_parms *cmd, void *mconfig, const char
*name){
if(!cmd || !mconfig || !name || !cmd->info){ return NULL; }
if(strlen(name)<=0){
return "Invalid Length of config option";
}
module_rec *tcfg = mconfig;
char **offset = NULL;
switch(*(int*)cmd->info){
case TRDOMAIN:
offset = &tcfg->moduledomain;
break;
case TRPATH:
offset = &tcfg->modulepath;
break;
case TRLOCALPATH:
offset = &tcfg->localpath;
break;
}
if(offset!=NULL){
(*offset) = apr_pstrdup(cmd->pool, name);
}
return NULL;
}
// set module enabled stuff
static const char* set_module_flags(cmd_parms *cmd, void *mconfig, int arg){
if(!cmd || !mconfig || !cmd->info){ return NULL; }
module_rec *tcfg = mconfig;
switch(*(int*)cmd->info){
case TRENABLE:
if(tcfg->enabled==2){
tcfg->enabled = arg;
}
break;
}
return NULL;
}
/* simple config options */
static const command_rec module_log_cmds[] = {
AP_INIT_TAKE1("ModuleDomain", set_module_strings, &tr_domain, OR_FILEINFO,
"domain to which module applies"),
AP_INIT_TAKE1("ModulePath", set_module_strings, &tr_path, OR_FILEINFO,
"Path for ModuleDomain to which module applies"),
AP_INIT_TAKE1("ModuleLocalPath", set_module_strings, &tr_localpath,
OR_FILEINFO,
"Path on local Filesystem where module will log tracked
data"),
AP_INIT_FLAG("Modulenable", set_module_flags, &tr_enable, OR_FILEINFO,
"whether or not to enable module"),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
ap_register_input_filter(mymoduleFilterName, handle_modulerequest, NULL,
AP_FTYPE_CONTENT_SET);
}
module AP_MODULE_DECLARE_DATA mymodule_module = {
STANDARD20_MODULE_STUFF,
make_module_dir, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
module_log_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
an strace of the segfault process looks like this:
accept(3, {sa_family=AF_INET6, sin6_port=htons(4906), inet_pton(AF_INET6,
"::ffff:192.168.11.200", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0},
[68719476764]) = 8
getsockname(8, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6,
"::ffff:192.168.11.77", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0},
[68719476764]) = 0
fcntl(8, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(8, F_SETFL, O_RDWR|O_NONBLOCK) = 0
read(8, "GET /somefile.html?"..., 8000) = 531
stat("/srv/www/htdocs/somefile.html", 0x7fbfffed70) = -1 ENOENT (No such file
or directory)
lstat("/srv", {st_mode=S_IFDIR|0755, st_size=96, ...}) = 0
lstat("/srv/www", {st_mode=S_IFDIR|0755, st_size=96, ...}) = 0
lstat("/srv/www/htdocs", {st_mode=S_IFDIR|0755, st_size=1448, ...}) = 0
lstat("/srv/www/htdocs/somefile.html", 0x7fbfffed60) = -1 ENOENT (No such file
or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
chdir("/srv/www") = 0
rt_sigaction(SIGSEGV, {SIG_DFL}, {SIG_DFL}, 8) = 0
kill(3449, SIGSEGV) = 0
rt_sigreturn(0xd79) = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
_________________________________________________________________
Connect to the next generation of MSN Messenger
http://imagine-msn.com/messenger/launch80/default.aspx?locale=en-us&source=wlmailtagline