hiho@ll 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 ^^) thx@ll 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 Get it now! |