libclamav/stats.c
f2571e34
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <sys/types.h>
c8bf9b6c
 #if !defined(_WIN32)
 #include <sys/sysctl.h>
 #include <dlfcn.h>
 #endif
f2571e34
 
 #ifdef CL_THREAD_SAFE
 #include <pthread.h>
 #endif
 
68e60b12
 #include <errno.h>
 
f2571e34
 #include "libclamav/others.h"
 #include "libclamav/clamav.h"
 #include "libclamav/json.h"
4473a0a9
 #include "libclamav/stats.h"
f2571e34
 
 static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type);
 void free_sample(cli_flagged_sample_t *sample);
 
 void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata)
 {
     cli_intel_t *intel;
     cli_flagged_sample_t *sample;
     size_t i;
     char **p;
4473a0a9
     int err, submit=0;
f2571e34
 
     if (!(cbdata))
         return;
 
     intel = (cli_intel_t *)cbdata;
4473a0a9
     if (!(intel->engine))
         return;
f2571e34
 
4473a0a9
     /* 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);
     }
f2571e34
 
4473a0a9
     if (submit) {
         if ((intel->engine->cb_stats_submit)) {
             intel->engine->cb_stats_submit(intel->engine, cbdata);
         } else {
f2571e34
             if ((intel->engine->cb_stats_flush))
                 intel->engine->cb_stats_flush(intel->engine, intel);
 
             return;
         }
     }
 
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_lock(&(intel->mutex));
     if (err) {
         cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
         return;
     }
 #endif
 
     sample = find_sample(intel, virname, md5, size, type);
     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++)
                 ;
         } 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);
             free(sample->virus_name);
             if (sample == intel->samples)
                 intel->samples = NULL;
 
             goto end;
         }
 
d45267e4
         p = realloc(sample->virus_name, sizeof(char **) * (i == 0 ? 2 : i+1));
f2571e34
         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->type = type;
         sample->size = size;
         intel->nsamples++;
     }
 
     cli_warnmsg("Added %s to the stats cache\n", (virname != NULL) ? virname: "[unknown]");
 
     sample->hits++;
 
 end:
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_unlock(&(intel->mutex));
     if (err) {
         cli_warnmsg("clamav_stats_add_sample: unlcoking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
     }
 #endif
 }
 
 void clamav_stats_flush(struct cl_engine *engine, void *cbdata)
 {
     cli_intel_t *intel;
     cli_flagged_sample_t *sample, *next;
68e60b12
     int err;
 
     if (!(cbdata) || !(engine))
         return;
 
     intel = (cli_intel_t *)cbdata;
f2571e34
 
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_lock(&(intel->mutex));
     if (err) {
         cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
         return;
     }
 #endif
 
     for (sample=intel->samples; sample != NULL; sample = next) {
         next = sample->next;
 
         free_sample(sample);
     }
 
     intel->samples = NULL;
     intel->nsamples = 0;
 
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_unlock(&(intel->mutex));
     if (err)
         cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
 #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);
     }
 
     free(sample);
 }
 
 void clamav_stats_submit(struct cl_engine *engine, void *cbdata)
 {
     char *json;
     cli_intel_t *intel;
68e60b12
     int err;
f2571e34
 
     intel = (cli_intel_t *)cbdata;
 
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_lock(&(intel->mutex));
     if (err) {
         cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
 
         if ((intel->engine) && (intel->engine->cb_stats_flush))
             intel->engine->cb_stats_flush(intel->engine, cbdata);
 
         return;
     }
 #endif
 
     json = export_stats_to_json(engine, (cli_intel_t *)cbdata);
 
 #ifdef CL_THREAD_SAFE
68e60b12
     err = pthread_mutex_unlock(&(intel->mutex));
     if (err) {
         cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
f2571e34
     }
 
 #endif
 
     cli_warnmsg("--- JSON ---\n%s\n--- END JSON ---\n", json);
 
     if (json)
         free(json);
 
     if ((engine->cb_stats_flush))
         engine->cb_stats_flush(engine, cbdata);
 }
 
4473a0a9
 void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, 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
 
     sample = find_sample(intel, virname, md5, size, type);
     if (!(sample))
         return;
 
     if (sample->prev)
         sample->prev->next = sample->next;
     if (sample->next)
         sample->next->prev = sample;
     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, cli_intel_sample_type_t type, 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, type);
     if (!(sample))
         return;
 
     if (sample->hits == 1) {
         if ((intel->engine->cb_stats_remove_sample))
             intel->engine->cb_stats_remove_sample(virname, md5, size, type, intel);
         else
             clamav_stats_remove_sample(virname, md5, size, type, intel);
 
         return;
     }
 
     sample->hits--;
 
 #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
 }
 
 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;
 
c8bf9b6c
     sz = sizeof(cli_intel_t);
 
4473a0a9
 #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));
c8bf9b6c
         return sz;
4473a0a9
     }
 #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;
 }
 
c8bf9b6c
 #if defined(_WIN32)
 char *clamav_stats_get_hostid(void *cbdata)
 {
     return strdup(STATS_ANON_UUID);
 }
 #else
 char *clamav_stats_get_hostid(void *cbdata)
 {
     char *sysctls[] = {
         "kern.hostuuid",
         NULL
     };
     size_t bufsz, i;
     char *buf;
 
02c1afff
 #if HAVE_SYSCTLBYNAME
     /* FreeBSD-landia */
     for (i=0; sysctls[i] != NULL; i++) {
         if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0))
             continue;
c8bf9b6c
 
02c1afff
         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;
c8bf9b6c
     }
02c1afff
 #else
         return strdup(STATS_ANON_UUID);
 #endif
c8bf9b6c
 
     return strdup(STATS_ANON_UUID);
 }
 #endif
 
f2571e34
 static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type)
 {
     cli_flagged_sample_t *sample;
     size_t i;
 
     for (sample = intel->samples; sample != NULL; sample = sample->next) {
         if (sample->type != type)
             continue;
 
         if (sample->size != size)
             continue;
 
         if (memcmp(sample->md5, md5, sizeof(sample->md5)))
             continue;
 
         if (!(virname))
             return sample;
 
         for (i=0; sample->virus_name[i] != NULL; i++)
             if (!strcmp(sample->virus_name[i], virname))
                 return sample;
     }
 
     return NULL;
 }