Index: shared/cfgparser.c =================================================================== --- shared/cfgparser.c (revision 3553) +++ shared/cfgparser.c (working copy) @@ -118,6 +118,7 @@ {"LocalIPAddress", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, {"ConnectTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM}, {"ReceiveTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM}, + {"ReloadDatabaseInBackground", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, {"DevACOnly", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, {"DevACDepth", OPT_NUM, -1, NULL, 0, OPT_CLAMD}, Index: clamd/server.h =================================================================== --- clamd/server.h (revision 3553) +++ clamd/server.h (working copy) @@ -51,6 +51,18 @@ unsigned int options; }; +/* reload thread arguments */ +struct rthrarg { + struct cl_engine **engine; + const struct cfgstruct *copt; + unsigned int dboptions; +#ifdef CLAMUKO + pthread_t *clamuko_pid; + pthread_attr_t *clamuko_attr; + struct thrarg *tharg; +#endif +}; + int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigned int dboptions, const struct cfgstruct *copt); void sighandler(int sig); void sighandler_th(int sig); Index: clamd/server-th.c =================================================================== --- clamd/server-th.c (revision 3553) +++ clamd/server-th.c (working copy) @@ -66,10 +66,12 @@ int progexit = 0; pthread_mutex_t exit_mutex; int reload = 0; +int reloading = 0; time_t reloaded_time = 0; pthread_mutex_t reload_mutex; int sighup = 0; static struct cl_stat *dbstat = NULL; +pthread_mutex_t engine_mutex; typedef struct client_conn_tag { int sd; @@ -255,6 +257,53 @@ return engine; } +void reload_thread(void *arg) +{ + struct cl_engine *newengine = NULL; + struct cl_engine *oldengine = NULL; + struct rthrarg *args = (struct rthrarg *) arg; + int ret = 0; + + newengine = reload_db(newengine, args->dboptions, args->copt, FALSE, &ret); + if(ret) { + logg("Terminating because of a fatal error.\n"); + pthread_mutex_lock(&exit_mutex); + progexit = 1; + pthread_mutex_unlock(&exit_mutex); + free(args); + pthread_exit(NULL); + } + + pthread_mutex_lock(&engine_mutex); + oldengine = *args->engine; + *args->engine = newengine; + pthread_mutex_unlock(&engine_mutex); + + if(oldengine) { + cl_free(oldengine); + oldengine = NULL; + } + + pthread_mutex_lock(&reload_mutex); + time(&reloaded_time); + reloading = 0; + pthread_mutex_unlock(&reload_mutex); + +#ifdef CLAMUKO + if(cfgopt(args->copt, "ClamukoScanOnAccess")->enabled) { + logg("Stopping and restarting Clamuko.\n"); + pthread_kill(*args->clamuko_pid, SIGUSR1); + pthread_join(*args->clamuko_pid, NULL); + pthread_mutex_lock(&engine_mutex); + args->tharg->engine = *args->engine; + pthread_mutex_unlock(&engine_mutex); + pthread_create(args->clamuko_pid, args->clamuko_attr, clamukoth, args->tharg); + } +#endif + free(args); + pthread_exit(NULL); +} + int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigned int dboptions, const struct cfgstruct *copt) { int max_threads, i, ret = 0; @@ -288,6 +337,11 @@ struct thrarg *tharg = NULL; /* shut up gcc */ #endif + pthread_t reload_pid; + pthread_attr_t reload_attr; + struct rthrarg *rthrargs; + int new_sd; + #ifndef C_WINDOWS memset(&sigact, 0, sizeof(struct sigaction)); #endif @@ -460,7 +514,9 @@ tharg = (struct thrarg *) malloc(sizeof(struct thrarg)); tharg->copt = copt; + pthread_mutex_lock(&engine_mutex); tharg->engine = engine; + pthread_mutex_unlock(&engine_mutex); tharg->limits = &limits; tharg->options = options; @@ -498,7 +554,11 @@ pthread_mutex_init(&exit_mutex, NULL); pthread_mutex_init(&reload_mutex, NULL); + pthread_mutex_init(&engine_mutex, NULL); + pthread_attr_init(&reload_attr); + pthread_attr_setdetachstate(&reload_attr, PTHREAD_CREATE_JOINABLE); + idletimeout = cfgopt(copt, "IdleTimeout")->numarg; if((thr_pool=thrmgr_new(max_threads, idletimeout, scanner_thread)) == NULL) { @@ -513,7 +573,7 @@ struct stat st_buf; #endif int socketd = socketds[0]; - int new_sd = 0; + new_sd = 0; if(nsockets > 1) { int pollret = poll_fds(socketds, nsockets, -1, 1); @@ -563,8 +623,12 @@ client_conn->sd = new_sd; client_conn->options = options; client_conn->copt = copt; + pthread_mutex_lock(&engine_mutex); + pthread_mutex_lock(&reload_mutex); client_conn->engine = cl_dup(engine); client_conn->engine_timestamp = reloaded_time; + pthread_mutex_unlock(&reload_mutex); + pthread_mutex_unlock(&engine_mutex); client_conn->limits = &limits; client_conn->socketds = socketds; client_conn->nsockets = nsockets; @@ -577,54 +641,68 @@ pthread_mutex_lock(&exit_mutex); if(progexit) { - if (new_sd >= 0) { - close(new_sd); - } pthread_mutex_unlock(&exit_mutex); break; } pthread_mutex_unlock(&exit_mutex); - if(selfchk) { + pthread_mutex_lock(&reload_mutex); + if(!reloading && selfchk) { + pthread_mutex_unlock(&reload_mutex); time(¤t_time); if((current_time - start_time) > (time_t)selfchk) { + pthread_mutex_lock(&engine_mutex); if(reload_db(engine, dboptions, copt, TRUE, &ret)) { + pthread_mutex_unlock(&engine_mutex); pthread_mutex_lock(&reload_mutex); reload = 1; pthread_mutex_unlock(&reload_mutex); + } else { + pthread_mutex_unlock(&engine_mutex); } time(&start_time); } + } else { + pthread_mutex_unlock(&reload_mutex); } pthread_mutex_lock(&reload_mutex); - if(reload) { + if(!reloading && reload) { + reloading = 1; + reload = 0; pthread_mutex_unlock(&reload_mutex); - engine = reload_db(engine, dboptions, copt, FALSE, &ret); - if(ret) { - logg("Terminating because of a fatal error.\n"); - if(new_sd >= 0) - close(new_sd); + rthrargs = (struct rthrarg *) malloc(sizeof(struct rthrarg)); + if(!rthrargs) { + logg("!Can't allocate memory for reload thread arguments\n"); break; } - pthread_mutex_lock(&reload_mutex); - reload = 0; - time(&reloaded_time); - pthread_mutex_unlock(&reload_mutex); + pthread_mutex_lock(&engine_mutex); + rthrargs->engine = &engine; + pthread_mutex_unlock(&engine_mutex); + rthrargs->copt = copt; + rthrargs->dboptions = dboptions; #ifdef CLAMUKO - if(cfgopt(copt, "ClamukoScanOnAccess")->enabled) { - logg("Stopping and restarting Clamuko.\n"); - pthread_kill(clamuko_pid, SIGUSR1); - pthread_join(clamuko_pid, NULL); - tharg->engine = engine; - pthread_create(&clamuko_pid, &clamuko_attr, clamukoth, tharg); + rthrargs->clamuko_pid = &clamuko_pid; + rthrargs->clamuko_attr = &clamuko_attr; + rthrargs->tharg = tharg; +#endif + if(pthread_create(&reload_pid, &reload_attr, reload_thread, + (void *) rthrargs)) { + logg("!Can't create reload thread\n"); + break; } -#endif + if(!cfgopt(copt, "ReloadDatabaseInBackground")->enabled) { + pthread_join(reload_pid, NULL); + } } else { pthread_mutex_unlock(&reload_mutex); } } + if (new_sd >= 0) { + close(new_sd); + } + /* Destroy the thread manager. * This waits for all current tasks to end */ @@ -636,8 +714,10 @@ pthread_join(clamuko_pid, NULL); } #endif + pthread_mutex_lock(&engine_mutex); if(engine) cl_free(engine); + pthread_mutex_unlock(&engine_mutex); if(dbstat) cl_statfree(dbstat); Index: etc/clamd.conf =================================================================== --- etc/clamd.conf (revision 3553) +++ etc/clamd.conf (working copy) @@ -64,6 +64,11 @@ # Default: hardcoded (depends on installation options) #DatabaseDirectory /var/lib/clamav +# Reload the database in the background, not blocking new connections while +# this takes place. +# Default: no +#ReloadDatabaseInBackground yes + # The daemon works in a local OR a network mode. Due to security reasons we # recommend the local mode.