703 lines
18 KiB
C
703 lines
18 KiB
C
/*
|
|
* Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
|
|
*
|
|
* Author: Shawn Webb
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#if !defined(_WIN32)
|
|
#if defined(C_SOLARIS)
|
|
#include <sys/utsname.h>
|
|
#else
|
|
#if HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
#if HAVE_SYSCTLBYNAME
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
#endif
|
|
#else
|
|
#include <Windows.h>
|
|
#include <tchar.h>
|
|
#endif
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
#include "others.h"
|
|
#include "clamav.h"
|
|
#include "dconf.h"
|
|
#include "stats_json.h"
|
|
#include "stats.h"
|
|
#include "hostid_internal.h"
|
|
#include "www.h"
|
|
|
|
#define DEBUG_STATS 0
|
|
|
|
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections);
|
|
void free_sample(cli_flagged_sample_t *sample);
|
|
|
|
#if DEBUG_STATS
|
|
char *get_hash(unsigned char *md5)
|
|
{
|
|
char *hash;
|
|
int i;
|
|
|
|
hash = calloc(1, 33);
|
|
if (!(hash))
|
|
return NULL;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
sprintf(hash + (i * 2), "%02x", md5[i]);
|
|
|
|
return hash;
|
|
}
|
|
|
|
char *get_sample_names(char **names)
|
|
{
|
|
char *ret;
|
|
size_t n, i, sz;
|
|
|
|
sz = 0;
|
|
for (n = 0; names[n] != NULL; n++)
|
|
sz += strlen(names[n]);
|
|
|
|
ret = calloc(1, sz + n + 1);
|
|
if (!(ret))
|
|
return NULL;
|
|
|
|
for (i = 0; names[i] != NULL; i++)
|
|
sprintf(ret + strlen(ret), "%s%s", (i == 0) ? "" : " ", names[i]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void print_sample(cli_flagged_sample_t *sample)
|
|
{
|
|
char *hash, *names;
|
|
size_t i;
|
|
|
|
if (!(sample))
|
|
return;
|
|
|
|
hash = get_hash(sample->md5);
|
|
if (!(hash))
|
|
return;
|
|
|
|
cli_warnmsg("Sample[%s]:\n", hash);
|
|
cli_warnmsg(" * Size: %zu\n", sample->size);
|
|
cli_warnmsg(" * Hits: %u\n", sample->hits);
|
|
|
|
free(hash);
|
|
|
|
names = get_sample_names(sample->virus_name);
|
|
if ((names))
|
|
cli_warnmsg(" * Names: %s\n", names);
|
|
|
|
if (sample->sections && sample->sections->nsections) {
|
|
for (i = 0; i < sample->sections->nsections; i++) {
|
|
hash = get_hash(sample->sections->sections[i].md5);
|
|
if ((hash)) {
|
|
cli_warnmsg(" * Section[%zu] (%zu): %s\n", i, sample->sections->sections[i].len, hash);
|
|
free(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((names))
|
|
free(names);
|
|
}
|
|
#endif
|
|
|
|
void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections, void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
cli_flagged_sample_t *sample;
|
|
size_t i;
|
|
char **p;
|
|
int err, submit = 0;
|
|
|
|
if (!(cbdata))
|
|
return;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
if (!(intel->engine))
|
|
return;
|
|
|
|
if (intel->engine->dconf->stats & DCONF_STATS_DISABLED)
|
|
return;
|
|
|
|
/* First check if we need to submit stats based on memory/number limits */
|
|
if ((intel->engine->cb_stats_get_size))
|
|
submit = (intel->engine->cb_stats_get_size(cbdata) >= intel->maxmem);
|
|
else
|
|
submit = (clamav_stats_get_size(cbdata) >= intel->maxmem);
|
|
|
|
if (submit == 0) {
|
|
if ((intel->engine->cb_stats_get_num))
|
|
submit = (intel->engine->cb_stats_get_num(cbdata) >= intel->maxsamples);
|
|
else
|
|
submit = (clamav_stats_get_num(cbdata) >= intel->maxsamples);
|
|
}
|
|
|
|
if (submit) {
|
|
if ((intel->engine->cb_stats_submit)) {
|
|
intel->engine->cb_stats_submit(intel->engine, cbdata);
|
|
} else {
|
|
if ((intel->engine->cb_stats_flush))
|
|
intel->engine->cb_stats_flush(intel->engine, intel);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
sample = find_sample(intel, virname, md5, size, sections);
|
|
if (!(sample)) {
|
|
if (!(intel->samples)) {
|
|
sample = intel->samples = calloc(1, sizeof(cli_flagged_sample_t));
|
|
if (!(sample))
|
|
goto end;
|
|
} else {
|
|
sample = calloc(1, sizeof(cli_flagged_sample_t));
|
|
if (!(sample))
|
|
goto end;
|
|
|
|
sample->next = intel->samples;
|
|
intel->samples->prev = sample;
|
|
intel->samples = sample;
|
|
}
|
|
|
|
if ((sample->virus_name)) {
|
|
for (i = 0; sample->virus_name[i] != NULL; i++)
|
|
;
|
|
p = realloc(sample->virus_name, sizeof(char **) * (i + 1));
|
|
if (!(p)) {
|
|
free(sample->virus_name);
|
|
free(sample);
|
|
if (sample == intel->samples)
|
|
intel->samples = NULL;
|
|
|
|
goto end;
|
|
}
|
|
|
|
sample->virus_name = p;
|
|
} else {
|
|
i = 0;
|
|
sample->virus_name = calloc(1, sizeof(char **));
|
|
if (!(sample->virus_name)) {
|
|
free(sample);
|
|
if (sample == intel->samples)
|
|
intel->samples = NULL;
|
|
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
sample->virus_name[i] = strdup((virname != NULL) ? virname : "[unknown]");
|
|
if (!(sample->virus_name[i])) {
|
|
free(sample->virus_name);
|
|
free(sample);
|
|
if (sample == intel->samples)
|
|
intel->samples = NULL;
|
|
|
|
goto end;
|
|
}
|
|
|
|
p = realloc(sample->virus_name, sizeof(char **) * (i + 2));
|
|
if (!(p)) {
|
|
free(sample->virus_name);
|
|
free(sample);
|
|
if (sample == intel->samples)
|
|
intel->samples = NULL;
|
|
|
|
goto end;
|
|
}
|
|
|
|
sample->virus_name = p;
|
|
sample->virus_name[i + 1] = NULL;
|
|
|
|
memcpy(sample->md5, md5, sizeof(sample->md5));
|
|
sample->size = (uint32_t)size;
|
|
intel->nsamples++;
|
|
|
|
if (sections && sections->nsections && !(sample->sections)) {
|
|
/* Copy the section data that has already been allocated. We don't care if calloc fails; just skip copying if it does. */
|
|
sample->sections = calloc(1, sizeof(stats_section_t));
|
|
if ((sample->sections)) {
|
|
sample->sections->sections = calloc(sections->nsections, sizeof(struct cli_section_hash));
|
|
if ((sample->sections->sections)) {
|
|
memcpy(sample->sections->sections, sections->sections, sections->nsections * sizeof(struct cli_section_hash));
|
|
sample->sections->nsections = sections->nsections;
|
|
} else {
|
|
free(sample->sections);
|
|
sample->sections = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sample->hits++;
|
|
|
|
end:
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_add_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void clamav_stats_flush(struct cl_engine *engine, void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
cli_flagged_sample_t *sample, *next;
|
|
int err;
|
|
|
|
if (!(cbdata) || !(engine))
|
|
return;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
for (sample = intel->samples; sample != NULL; sample = next) {
|
|
next = sample->next;
|
|
|
|
free_sample(sample);
|
|
}
|
|
|
|
intel->samples = NULL;
|
|
intel->nsamples = 0;
|
|
if (intel->hostid) {
|
|
free(intel->hostid);
|
|
intel->hostid = NULL;
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err)
|
|
cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
#endif
|
|
}
|
|
|
|
void free_sample(cli_flagged_sample_t *sample)
|
|
{
|
|
size_t i;
|
|
|
|
if ((sample->virus_name)) {
|
|
for (i = 0; sample->virus_name[i] != NULL; i++)
|
|
free(sample->virus_name[i]);
|
|
|
|
free(sample->virus_name);
|
|
}
|
|
|
|
if ((sample->sections) && (sample->sections->nsections)) {
|
|
free(sample->sections->sections);
|
|
free(sample->sections);
|
|
}
|
|
|
|
free(sample);
|
|
}
|
|
|
|
void clamav_stats_submit(struct cl_engine *engine, void *cbdata)
|
|
{
|
|
char *json;
|
|
cli_intel_t *intel, myintel;
|
|
cli_flagged_sample_t *sample, *next;
|
|
int err;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
if (!(intel) || !(engine))
|
|
return;
|
|
|
|
if (engine->dconf->stats & DCONF_STATS_DISABLED)
|
|
return;
|
|
|
|
if (!(engine->cb_stats_get_hostid)) {
|
|
/* Submitting stats is disabled due to HostID being turned off */
|
|
if ((engine->cb_stats_flush))
|
|
engine->cb_stats_flush(engine, cbdata);
|
|
|
|
return;
|
|
}
|
|
|
|
cli_dbgmsg("stats - start\n");
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
|
|
if ((intel->engine) && (intel->engine->cb_stats_flush))
|
|
intel->engine->cb_stats_flush(intel->engine, cbdata);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* Empty out the cached intelligence data so that other threads don't sit waiting to add data to the cache */
|
|
memcpy(&myintel, intel, sizeof(cli_intel_t));
|
|
intel->samples = NULL;
|
|
intel->nsamples = 0;
|
|
|
|
json = export_stats_to_json(engine, &myintel);
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
}
|
|
#endif
|
|
|
|
for (sample = myintel.samples; sample != NULL; sample = next) {
|
|
#if DEBUG_STATS
|
|
print_sample(sample);
|
|
#endif
|
|
next = sample->next;
|
|
|
|
free_sample(sample);
|
|
}
|
|
|
|
if (json) {
|
|
submit_post(STATS_HOST, STATS_PORT, "PUT", "/clamav/1/submit/stats", json, myintel.timeout);
|
|
free(json);
|
|
}
|
|
|
|
if (myintel.hostid && !(intel->hostid)) {
|
|
free(myintel.hostid);
|
|
myintel.hostid = NULL;
|
|
}
|
|
|
|
cli_dbgmsg("stats - end\n");
|
|
}
|
|
|
|
void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
cli_flagged_sample_t *sample;
|
|
int err;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
if (!(intel))
|
|
return;
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_remove_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
while ((sample = find_sample(intel, virname, md5, size, NULL))) {
|
|
if (sample->prev)
|
|
sample->prev->next = sample->next;
|
|
if (sample->next)
|
|
sample->next->prev = sample->prev;
|
|
if (sample == intel->samples)
|
|
intel->samples = sample->next;
|
|
|
|
free_sample(sample);
|
|
intel->nsamples--;
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_remove_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void clamav_stats_decrement_count(const char *virname, const unsigned char *md5, size_t size, void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
cli_flagged_sample_t *sample;
|
|
int err;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
if (!(intel))
|
|
return;
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_decrement_count: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
sample = find_sample(intel, virname, md5, size, NULL);
|
|
if (!(sample))
|
|
goto clamav_stats_decrement_end;
|
|
|
|
if (sample->hits == 1) {
|
|
if ((intel->engine->cb_stats_remove_sample))
|
|
intel->engine->cb_stats_remove_sample(virname, md5, size, intel);
|
|
else
|
|
clamav_stats_remove_sample(virname, md5, size, intel);
|
|
|
|
goto clamav_stats_decrement_end;
|
|
}
|
|
|
|
sample->hits--;
|
|
|
|
clamav_stats_decrement_end:
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_decrement_count: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
size_t clamav_stats_get_num(void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
|
|
if (!(intel))
|
|
return 0;
|
|
|
|
return intel->nsamples;
|
|
}
|
|
|
|
size_t clamav_stats_get_size(void *cbdata)
|
|
{
|
|
cli_intel_t *intel;
|
|
cli_flagged_sample_t *sample;
|
|
size_t sz, i;
|
|
int err;
|
|
|
|
intel = (cli_intel_t *)cbdata;
|
|
if (!(intel))
|
|
return 0;
|
|
|
|
sz = sizeof(cli_intel_t);
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_lock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_get_size: locking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
return sz;
|
|
}
|
|
#endif
|
|
|
|
for (sample = intel->samples; sample != NULL; sample = sample->next) {
|
|
sz += sizeof(cli_flagged_sample_t);
|
|
if ((sample->virus_name)) {
|
|
for (i = 0; sample->virus_name[i] != NULL; i++)
|
|
sz += strlen(sample->virus_name[i]);
|
|
sz += sizeof(char **) * i;
|
|
}
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
err = pthread_mutex_unlock(&(intel->mutex));
|
|
if (err) {
|
|
cli_warnmsg("clamav_stats_get_size: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
|
|
}
|
|
#endif
|
|
|
|
return sz;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
char *clamav_stats_get_hostid(void *cbdata)
|
|
{
|
|
HW_PROFILE_INFO HwProfInfo;
|
|
|
|
if (!GetCurrentHwProfile(&HwProfInfo))
|
|
return strdup(STATS_ANON_UUID);
|
|
|
|
return strdup(HwProfInfo.szHwProfileGuid);
|
|
}
|
|
#elif defined(C_SOLARIS)
|
|
char *clamav_stats_get_hostid(void *cbdata)
|
|
{
|
|
struct utsname utsnm;
|
|
int ret;
|
|
|
|
ret = uname(&utsnm);
|
|
if (ret != -1)
|
|
return strdup(utsnm.nodename);
|
|
|
|
return strdup(STATS_ANON_UUID);
|
|
}
|
|
#else
|
|
char *clamav_stats_get_hostid(void *cbdata)
|
|
{
|
|
char *sysctls[] = {
|
|
"kern.hostuuid",
|
|
NULL};
|
|
size_t bufsz, i;
|
|
char *buf;
|
|
|
|
UNUSEDPARAM(cbdata);
|
|
|
|
#if HAVE_SYSCTLBYNAME
|
|
/*
|
|
* FreeBSD provides a handy-dandy sysctl for grabbing the system's HostID. In a jail that
|
|
* hasn't run the hostid rc.d script, the hostid defaults to all zeros.
|
|
*/
|
|
for (i = 0; sysctls[i] != NULL; i++) {
|
|
if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0))
|
|
continue;
|
|
|
|
break; /* Got one */
|
|
}
|
|
|
|
if (sysctls[i] != NULL) {
|
|
buf = calloc(1, bufsz + 1);
|
|
if (sysctlbyname(sysctls[i], buf, &bufsz, NULL, 0))
|
|
return strdup(STATS_ANON_UUID); /* Not sure why this would happen, but we'll just default to the anon uuid on error */
|
|
|
|
return buf;
|
|
}
|
|
|
|
return strdup(STATS_ANON_UUID);
|
|
#else
|
|
buf = internal_get_host_id();
|
|
if (!(buf))
|
|
return strdup(STATS_ANON_UUID);
|
|
return buf;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections)
|
|
{
|
|
cli_flagged_sample_t *sample;
|
|
size_t i;
|
|
|
|
for (sample = intel->samples; sample != NULL; sample = sample->next) {
|
|
int foundSections = 0;
|
|
|
|
if (sample->size != size)
|
|
continue;
|
|
|
|
if (memcmp(sample->md5, md5, sizeof(sample->md5)))
|
|
continue;
|
|
|
|
if (!(virname))
|
|
return sample;
|
|
|
|
if ((sections) && (sample->sections)) {
|
|
if (sections->nsections == sample->sections->nsections) {
|
|
for (i = 0; i < sections->nsections; i++)
|
|
if (sections->sections[i].len == sample->sections->sections[i].len)
|
|
if (memcmp(sections->sections[i].md5, sample->sections->sections[i].md5, sizeof(stats_section_t)))
|
|
break;
|
|
|
|
if (i == sections->nsections)
|
|
foundSections = 1;
|
|
}
|
|
} else {
|
|
foundSections = 1;
|
|
}
|
|
|
|
if (foundSections)
|
|
for (i = 0; sample->virus_name[i] != NULL; i++)
|
|
if (!strcmp(sample->virus_name[i], virname))
|
|
return sample;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_submit(struct cl_engine *engine, clcb_stats_submit callback)
|
|
{
|
|
engine->cb_stats_submit = callback;
|
|
}
|
|
|
|
void cl_engine_set_stats_set_cbdata(struct cl_engine *engine, void *cbdata)
|
|
{
|
|
engine->stats_data = cbdata;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_add_sample(struct cl_engine *engine, clcb_stats_add_sample callback)
|
|
{
|
|
engine->cb_stats_add_sample = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_remove_sample(struct cl_engine *engine, clcb_stats_remove_sample callback)
|
|
{
|
|
engine->cb_stats_remove_sample = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_decrement_count(struct cl_engine *engine, clcb_stats_decrement_count callback)
|
|
{
|
|
engine->cb_stats_decrement_count = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_flush(struct cl_engine *engine, clcb_stats_flush callback)
|
|
{
|
|
engine->cb_stats_flush = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_get_num(struct cl_engine *engine, clcb_stats_get_num callback)
|
|
{
|
|
engine->cb_stats_get_num = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_get_size(struct cl_engine *engine, clcb_stats_get_size callback)
|
|
{
|
|
engine->cb_stats_get_size = callback;
|
|
}
|
|
|
|
void cl_engine_set_clcb_stats_get_hostid(struct cl_engine *engine, clcb_stats_get_hostid callback)
|
|
{
|
|
engine->cb_stats_get_hostid = callback;
|
|
}
|
|
|
|
void cl_engine_stats_enable(struct cl_engine *engine)
|
|
{
|
|
engine->cb_stats_add_sample = clamav_stats_add_sample;
|
|
engine->cb_stats_submit = clamav_stats_submit;
|
|
}
|