clamscan/manager.c
e3aaff8e
 /*
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
b89ebf3c
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
086eab5c
  *
  *  Authors: Tomasz Kojm
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  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
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  *
  */
 
5c07666a
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <sys/stat.h>
 #include <sys/types.h>
8c57a6c1
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
4cd80898
 #include <dirent.h>
 #ifndef _WIN32
e3aaff8e
 #include <sys/wait.h>
4790a32f
 #include <utime.h>
9a03413e
 #include <sys/time.h>
 #include <sys/resource.h>
34f71e0e
 #endif
e3aaff8e
 #include <fcntl.h>
34f71e0e
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
34f71e0e
 #endif
e3aaff8e
 #include <sys/types.h>
 #include <signal.h>
 #include <errno.h>
8c57a6c1
 #include <target.h>
e3aaff8e
 
 #include "manager.h"
7a2997f1
 #include "global.h"
 
269d520d
 #include "shared/optparser.h"
ee6702ab
 #include "shared/actions.h"
7a2997f1
 #include "shared/output.h"
 #include "shared/misc.h"
 
 #include "libclamav/clamav.h"
 #include "libclamav/others.h"
 #include "libclamav/matcher-ac.h"
5c2c7233
 #include "libclamav/matcher-pcre.h"
7a2997f1
 #include "libclamav/str.h"
05f92e64
 #include "libclamav/readdb.h"
e3aaff8e
 
 #ifdef C_LINUX
 dev_t procdev;
 #endif
 
c4cd560b
 char hostid[37];
 
6df13d04
 int is_valid_hostid(void);
c4cd560b
 char *get_hostid(void *cbdata);
 
8c57a6c1
 #ifdef _WIN32
 /* FIXME: If possible, handle users correctly */
 static int checkaccess(const char *path, const char *username, int mode)
 {
4b6af09e
     return !access(path, mode);
8c57a6c1
 }
 #else
 static int checkaccess(const char *path, const char *username, int mode)
 {
8b5746bc
     struct passwd *user;
     int ret = 0, status;
8c57a6c1
 
     if(!geteuid()) {
8b5746bc
         if((user = getpwnam(username)) == NULL) {
             return -1;
         }
8c57a6c1
 
8b5746bc
         switch(fork()) {
         case -1:
             return -2;
         case 0:
             if(setgid(user->pw_gid)) {
                 fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
                 exit(0);
             }
8c57a6c1
 
8b5746bc
             if(setuid(user->pw_uid)) {
                 fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
                 exit(0);
             }
8c57a6c1
 
8b5746bc
             if(access(path, mode))
                 exit(0);
             else
                 exit(1);
         default:
             wait(&status);
             if(WIFEXITED(status) && WEXITSTATUS(status) == 1)
                 ret = 1;
         }
8c57a6c1
     } else {
8b5746bc
         if(!access(path, mode))
             ret = 1;
8c57a6c1
     }
 
     return ret;
 }
 #endif
 
248c2f4d
 struct metachain {
     char **chains;
814dbe6f
     size_t lastadd;
     size_t lastvir;
     size_t level;
     size_t nchains;
248c2f4d
 };
 
1f1bf36b
 struct clamscan_cb_data {
     struct metachain * chain;
059ca614
     const char * filename;
1f1bf36b
 };
 
248c2f4d
 static cl_error_t pre(int fd, const char *type, void *context)
 {
814dbe6f
     struct metachain *c;
1f1bf36b
     struct clamscan_cb_data *d;
814dbe6f
 
6df13d04
     UNUSEDPARAM(fd);
859b6fb8
     UNUSEDPARAM(type);
6df13d04
 
814dbe6f
     if (!(context))
         return CL_CLEAN;
1f1bf36b
     d = (struct clamscan_cb_data *)context;
     c = d->chain;
     if (c == NULL)
         return CL_CLEAN;
814dbe6f
 
     c->level++;
 
248c2f4d
     return CL_CLEAN;
 }
 
814dbe6f
 static int print_chain(struct metachain *c, char *str, size_t len)
248c2f4d
 {
814dbe6f
     size_t i;
     size_t na = 0;
 
     for (i=0;i<c->nchains-1;i++) {
         size_t n = strlen(c->chains[i]);
8b5746bc
 
         if (na)
             str[na++] = '!';
 
         if (n + na + 2 > len)
             break;
 
         memcpy(str + na, c->chains[i], n);
         na += n;
248c2f4d
     }
8b5746bc
 
248c2f4d
     str[na] = '\0';
     str[len-1] = '\0';
8b5746bc
 
814dbe6f
     return i == c->nchains-1 ? 0 : 1;
248c2f4d
 }
 
 static cl_error_t post(int fd, int result, const char *virname, void *context)
 {
1f1bf36b
     struct clamscan_cb_data *d = context;
c9a070c9
     struct metachain *c = NULL;
814dbe6f
     char str[128];
8b5746bc
 
6df13d04
     UNUSEDPARAM(fd);
     UNUSEDPARAM(result);
8b5746bc
 
1f1bf36b
     if (d != NULL)
         c = d->chain;
 
814dbe6f
     if (c && c->nchains) {
8b5746bc
         print_chain(c, str, sizeof(str));
814dbe6f
 
8b5746bc
         if (c->level == c->lastadd && !virname)
814dbe6f
             free(c->chains[--c->nchains]);
 
8b5746bc
         if (virname && !c->lastvir)
             c->lastvir = c->level;
248c2f4d
     }
8b5746bc
 
248c2f4d
     if (c)
8b5746bc
         c->level--;
 
248c2f4d
     return CL_CLEAN;
 }
 
 static cl_error_t meta(const char* container_type, unsigned long fsize_container, const char *filename,
814dbe6f
     unsigned long fsize_real,  int is_encrypted, unsigned int filepos_container, void *context)
248c2f4d
 {
     char prev[128];
814dbe6f
     struct metachain *c;
1f1bf36b
     struct clamscan_cb_data *d;
814dbe6f
     const char *type;
     size_t n;
248c2f4d
     char *chain;
     char **chains;
     int toolong;
 
6df13d04
     UNUSEDPARAM(fsize_container);
     UNUSEDPARAM(fsize_real);
     UNUSEDPARAM(is_encrypted);
     UNUSEDPARAM(filepos_container);
 
1f1bf36b
     if (!(context))
         return CL_CLEAN;
     d = (struct clamscan_cb_data *)context;
 
     c = d->chain;
814dbe6f
     type = (strncmp(container_type, "CL_TYPE_", 8) == 0 ? container_type + 8 : container_type);
     n = strlen(type) + strlen(filename) + 2;
 
248c2f4d
     if (!c)
8b5746bc
         return CL_CLEAN;
 
248c2f4d
     chain = malloc(n);
8b5746bc
 
248c2f4d
     if (!chain)
8b5746bc
         return CL_CLEAN;
814dbe6f
 
248c2f4d
     if (!strcmp(type, "ANY"))
8b5746bc
         snprintf(chain, n,"%s", filename);
248c2f4d
     else
8b5746bc
         snprintf(chain, n,"%s:%s", type, filename);
 
248c2f4d
     if (c->lastadd != c->level) {
814dbe6f
         n = c->nchains + 1;
8b5746bc
 
         chains = realloc(c->chains, n * sizeof(*chains));
         if (!chains) {
             free(chain);
             return CL_CLEAN;
         }
 
         c->chains = chains;
814dbe6f
         c->nchains = n;
8b5746bc
         c->lastadd = c->level;
248c2f4d
     } else {
814dbe6f
         if (c->nchains > 0)
             free(c->chains[c->nchains-1]);
248c2f4d
     }
8b5746bc
 
814dbe6f
     if (c->nchains > 0) {
         c->chains[c->nchains-1] = chain;
         toolong = print_chain(c, prev, sizeof(prev));
         logg("*Scanning %s%s!%s\n", prev,toolong ? "..." : "", chain);
     } else {
         free(chain);
     }
8b5746bc
 
248c2f4d
     return CL_CLEAN;
 }
 
1f1bf36b
 static void clamscan_virus_found_cb(int fd, const char *virname, void *context)
 {
     struct clamscan_cb_data *data = (struct clamscan_cb_data *)context;
059ca614
     const char * filename;
1f1bf36b
 
     if (data == NULL)
         return;
     if (data->filename != NULL)
         filename = data->filename;
     else
         filename = "(filename not set)";
     logg("~%s: %s FOUND\n", filename, virname);
     return;
 }
 
d7979d4f
 static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
7a2997f1
 {
8b5746bc
     int ret = 0, fd, included;
     unsigned i;
     const struct optstruct *opt;
e551468a
     const char *virname = NULL;
8b5746bc
     STATBUF sb;
     struct metachain chain;
1f1bf36b
     struct clamscan_cb_data data;
ec6429ab
 
269d520d
     if((opt = optget(opts, "exclude"))->enabled) {
8b5746bc
         while(opt) {
             if(match_regex(filename, opt->strarg) == 1) {
                 if(!printinfected)
                     logg("~%s: Excluded\n", filename);
 
                 return;
             }
 
             opt = opt->nextarg;
         }
ec6429ab
     }
 
269d520d
     if((opt = optget(opts, "include"))->enabled) {
8b5746bc
         included = 0;
 
         while(opt) {
             if(match_regex(filename, opt->strarg) == 1) {
                 included = 1;
                 break;
             }
 
             opt = opt->nextarg;
         }
 
         if(!included) {
             if(!printinfected)
                 logg("~%s: Excluded\n", filename);
 
             return;
         }
ec6429ab
     }
 
8c57a6c1
     /* argh, don't scan /proc files */
d9b6b8c7
     if(CLAMSTAT(filename, &sb) != -1) {
8c57a6c1
 #ifdef C_LINUX
8b5746bc
         if(procdev && sb.st_dev == procdev) {
             if(!printinfected)
                 logg("~%s: Excluded (/proc)\n", filename);
 
             return;
         }
8c57a6c1
 #endif    
8b5746bc
         if(!sb.st_size) {
             if(!printinfected)
                 logg("~%s: Empty file\n", filename);
 
             return;
         }
 
         info.rblocks += sb.st_size / CL_COUNT_PRECISION;
ec6429ab
     }
8c57a6c1
 
be4bf7f4
 #ifndef _WIN32
8b5746bc
     if(geteuid()) {
         if(checkaccess(filename, NULL, R_OK) != 1) {
             if(!printinfected)
                 logg("~%s: Access denied\n", filename);
 
             info.errors++;
             return;
         }
     }
ec6429ab
 #endif
 
248c2f4d
     memset(&chain, 0, sizeof(chain));
     if(optget(opts, "archive-verbose")->enabled) {
814dbe6f
         chain.chains = malloc(sizeof(char **));
8b5746bc
         if (chain.chains) {
             chain.chains[0] = strdup(filename);
9c899efb
             if (!chain.chains[0]) {
                 free(chain.chains);
                 logg("Unable to allocate memory in scanfile()\n");
                 info.errors++;
                 return;
             }
814dbe6f
             chain.nchains = 1;
8b5746bc
         }
248c2f4d
     }
8b5746bc
 
ec6429ab
     logg("*Scanning %s\n", filename);
 
6e246c11
     if((fd = safe_open(filename, O_RDONLY|O_BINARY)) == -1) {
8b5746bc
         logg("^Can't open file %s: %s\n", filename, strerror(errno));
         info.errors++;
         return;
ec6429ab
     }
 
1f1bf36b
     data.chain = &chain;
     data.filename = filename;
d39cb658
     if((ret = cl_scandesc_callback(fd, filename, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) {
8b5746bc
         if(optget(opts, "archive-verbose")->enabled) {
814dbe6f
             if (chain.nchains > 1) {
8b5746bc
                 char str[128];
                 int toolong = print_chain(&chain, str, sizeof(str));
 
059ca614
                 logg("~%s%s!(%llu)%s: %s FOUND\n", str, toolong ? "..." : "", (long long unsigned)(chain.lastvir-1), chain.chains[chain.nchains-1], virname);
8b5746bc
             } else if (chain.lastvir) {
059ca614
                 logg("~%s!(%llu): %s FOUND\n", filename, (long long unsigned)(chain.lastvir-1), virname);
8b5746bc
             }
         }
         info.files++;
         info.ifiles++;
 
         if(bell)
             fprintf(stderr, "\007");
ec6429ab
     } else if(ret == CL_CLEAN) {
8b5746bc
         if(!printinfected && printclean)
             mprintf("~%s: OK\n", filename);
 
         info.files++;
8770404a
     } else {
8b5746bc
         if(!printinfected)
             logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
 
         info.errors++;
8770404a
     }
ec6429ab
 
814dbe6f
     for (i=0;i<chain.nchains;i++)
8b5746bc
         free(chain.chains[i]);
 
248c2f4d
     free(chain.chains);
ec6429ab
     close(fd);
 
ee6702ab
     if(ret == CL_VIRUS && action)
8b5746bc
         action(filename);
ec6429ab
 }
 
d7979d4f
 static void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options, unsigned int depth, dev_t dev)
ec6429ab
 {
8b5746bc
     DIR *dd;
     struct dirent *dent;
     STATBUF sb;
     char *fname;
     int included;
     const struct optstruct *opt;
     unsigned int dirlnk, filelnk;
ec6429ab
 
 
269d520d
     if((opt = optget(opts, "exclude-dir"))->enabled) {
8b5746bc
         while(opt) {
             if(match_regex(dirname, opt->strarg) == 1) {
                 if(!printinfected)
                     logg("~%s: Excluded\n", dirname);
 
                 return;
             }
 
             opt = opt->nextarg;
         }
ec6429ab
     }
 
269d520d
     if((opt = optget(opts, "include-dir"))->enabled) {
8b5746bc
         included = 0;
         while(opt) {
             if(match_regex(dirname, opt->strarg) == 1) {
                 included = 1;
                 break;
             }
 
             opt = opt->nextarg;
         }
 
         if(!included) {
             if(!printinfected)
                 logg("~%s: Excluded\n", dirname);
 
             return;
         }
ec6429ab
     }
 
269d520d
     if(depth > (unsigned int) optget(opts, "max-dir-recursion")->numarg)
8b5746bc
         return;
ec6429ab
 
8c57a6c1
     dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
     filelnk = optget(opts, "follow-file-symlinks")->numarg;
 
ec6429ab
     if((dd = opendir(dirname)) != NULL) {
8b5746bc
         info.dirs++;
         depth++;
         while((dent = readdir(dd))) {
             if(dent->d_ino) {
                 if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
                     /* build the full name */
                     fname = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
                     if (fname == NULL) { /* oops, malloc() failed, print warning and return */
                         logg("!scandirs: Memory allocation failed for fname\n");
                         break;
                     }
 
                     if(!strcmp(dirname, PATHSEP))
                         sprintf(fname, PATHSEP"%s", dent->d_name);
                     else
                         sprintf(fname, "%s"PATHSEP"%s", dirname, dent->d_name);
 
                     /* stat the file */
                     if(LSTAT(fname, &sb) != -1) {
                         if(!optget(opts, "cross-fs")->enabled) {
                             if(sb.st_dev != dev) {
                                 if(!printinfected)
                                     logg("~%s: Excluded\n", fname);
 
                                 free(fname);
                                 continue;
                             }
                         }
                         if(S_ISLNK(sb.st_mode)) {
                             if(dirlnk != 2 && filelnk != 2) {
                                 if(!printinfected)
                                     logg("%s: Symbolic link\n", fname);
                             } else if(CLAMSTAT(fname, &sb) != -1) {
                                 if(S_ISREG(sb.st_mode) && filelnk == 2) {
                                     scanfile(fname, engine, opts, options);
                                 } else if(S_ISDIR(sb.st_mode) && dirlnk == 2) {
                                     if(recursion)
                                         scandirs(fname, engine, opts, options, depth, dev);
                                 } else {
                                     if(!printinfected)
                                         logg("%s: Symbolic link\n", fname);
                                 }
                             }
                         } else if(S_ISREG(sb.st_mode)) {
                             scanfile(fname, engine, opts, options);
                         } else if(S_ISDIR(sb.st_mode) && recursion) {
                             scandirs(fname, engine, opts, options, depth, dev);
                         }
                     }
 
                     free(fname);
                 }
             }
         }
         closedir(dd);
ec6429ab
     } else {
8b5746bc
         if(!printinfected)
             logg("~%s: Can't open directory.\n", dirname);
 
         info.errors++;
ec6429ab
     }
7a2997f1
 }
 
d7979d4f
 static int scanstdin(const struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
7a2997f1
 {
8b5746bc
     int ret;
     unsigned int fsize = 0;
     const char *virname, *tmpdir;
     char *file, buff[FILEBUFF];
     size_t bread;
     FILE *fs;
1f1bf36b
     struct clamscan_cb_data data;
7a2997f1
 
269d520d
     if(optget(opts, "tempdir")->enabled) {
8b5746bc
         tmpdir = optget(opts, "tempdir")->strarg;
     } else {
         /* check write access */
         tmpdir = cli_gettmpdir();
     }
7a2997f1
 
     if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
8b5746bc
         logg("!Can't write to temporary directory\n");
         return 2;
7a2997f1
     }
 
f246a9c7
     if(!(file = cli_gentemp(tmpdir))) {
8b5746bc
         logg("!Can't generate tempfile name\n");
         return 2;
f246a9c7
     }
7a2997f1
 
     if(!(fs = fopen(file, "wb"))) {
8b5746bc
         logg("!Can't open %s for writing\n", file);
         free(file);
         return 2;
7a2997f1
     }
 
5da3127b
     while((bread = fread(buff, 1, FILEBUFF, stdin))) {
8b5746bc
         fsize += bread;
         if(fwrite(buff, 1, bread, fs) < bread) {
             logg("!Can't write to %s\n", file);
             free(file);
             fclose(fs);
             return 2;
         }
5da3127b
     }
8b5746bc
 
7a2997f1
     fclose(fs);
 
     logg("*Checking %s\n", file);
8b5746bc
 
7a2997f1
     info.files++;
5da3127b
     info.rblocks += fsize / CL_COUNT_PRECISION;
7a2997f1
 
1f1bf36b
     data.filename = "stdin";
     data.chain = NULL;
     if((ret = cl_scanfile_callback(file, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) {
8b5746bc
         info.ifiles++;
7a2997f1
 
8b5746bc
         if(bell)
             fprintf(stderr, "\007");
7a2997f1
     } else if(ret == CL_CLEAN) {
8b5746bc
         if(!printinfected)
             mprintf("stdin: OK\n");
88700e89
     } else {
8b5746bc
         if(!printinfected)
             logg("stdin: %s ERROR\n", cl_strerror(ret));
 
         info.errors++;
88700e89
     }
7a2997f1
 
     unlink(file);
     free(file);
     return ret;
 }
8000d078
 
269d520d
 int scanmanager(const struct optstruct *opts)
e3aaff8e
 {
8b5746bc
     int ret = 0, i;
d7979d4f
     struct cl_scan_options options;
     unsigned int dboptions = 0, dirlnk = 1, filelnk = 1;
8b5746bc
     struct cl_engine *engine;
     STATBUF sb;
     char *file, cwd[1024], *pua_cats = NULL;
     const char *filename;
     const struct optstruct *opt;
4cd80898
 #ifndef _WIN32
8b5746bc
     struct rlimit rlim;
9a03413e
 #endif
e3aaff8e
 
d7979d4f
     /* Initalize scan options struct */
     memset(&options, 0, sizeof(struct cl_scan_options));
 
8c57a6c1
     dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
     if(dirlnk > 2) {
8b5746bc
         logg("!--follow-dir-symlinks: Invalid argument\n");
         return 2;
8c57a6c1
     }
 
     filelnk = optget(opts, "follow-file-symlinks")->numarg;
     if(filelnk > 2) {
8b5746bc
         logg("!--follow-file-symlinks: Invalid argument\n");
         return 2;
8c57a6c1
     }
 
bbfac258
     if(optget(opts, "yara-rules")->enabled) {
 	char *p = optget(opts, "yara-rules")->strarg;
 	if(strcmp(p, "yes")) {
 	    if(!strcmp(p, "only"))
 		dboptions |= CL_DB_YARA_ONLY;
 	    else if (!strcmp(p, "no"))
 		dboptions |= CL_DB_YARA_EXCLUDE;
 	}
 
     }
 
269d520d
     if(optget(opts, "phishing-sigs")->enabled)
8b5746bc
         dboptions |= CL_DB_PHISHING;
e3aaff8e
 
208ceae5
     if(optget(opts, "official-db-only")->enabled)
8b5746bc
         dboptions |= CL_DB_OFFICIAL_ONLY;
208ceae5
 
269d520d
     if(optget(opts,"phishing-scan-urls")->enabled)
8b5746bc
         dboptions |= CL_DB_PHISHING_URLS;
a68507c5
 
52dd3a6b
     if(optget(opts,"bytecode")->enabled)
8b5746bc
         dboptions |= CL_DB_BYTECODE;
52dd3a6b
 
370892d0
     if((ret = cl_init(CL_INIT_DEFAULT))) {
8b5746bc
         logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
         return 2;
370892d0
     }
 
b8fe70b3
     if(!(engine = cl_engine_new())) {
8b5746bc
         logg("!Can't initialize antivirus engine\n");
         return 2;
370892d0
     }
 
312b7e53
     cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb);
     
34e9acb0
     if (optget(opts, "disable-cache")->enabled)
         cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
 
269d520d
     if(optget(opts, "detect-pua")->enabled) {
8b5746bc
         dboptions |= CL_DB_PUA;
         if((opt = optget(opts, "exclude-pua"))->enabled) {
             dboptions |= CL_DB_PUA_EXCLUDE;
             i = 0;
             while(opt) {
                 if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
                     logg("!Can't allocate memory for pua_cats\n");
 
                     cl_engine_free(engine);
                     return 2;
                 }
 
                 sprintf(pua_cats + i, ".%s", opt->strarg);
                 i += strlen(opt->strarg) + 1;
                 pua_cats[i] = 0;
 
                 opt = opt->nextarg;
             }
             pua_cats[i] = '.';
             pua_cats[i + 1] = 0;
         }
 
         if((opt = optget(opts, "include-pua"))->enabled) {
             if(pua_cats) {
                 logg("!--exclude-pua and --include-pua cannot be used at the same time\n");
 
                 cl_engine_free(engine);
                 free(pua_cats);
                 return 2;
             }
 
             dboptions |= CL_DB_PUA_INCLUDE;
             i = 0;
             while(opt) {
                 if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
                     logg("!Can't allocate memory for pua_cats\n");
                     cl_engine_free(engine);
                     return 2;
                 }
 
                 sprintf(pua_cats + i, ".%s", opt->strarg);
                 i += strlen(opt->strarg) + 1;
                 pua_cats[i] = 0;
 
                 opt = opt->nextarg;
             }
 
             pua_cats[i] = '.';
             pua_cats[i + 1] = 0;
         }
 
         if(pua_cats) {
             if((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
                 logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
 
                 free(pua_cats);
                 cl_engine_free(engine);
                 return 2;
             }
 
             free(pua_cats);
         }
b023c36d
     }
 
2accc66f
     if(optget(opts, "dev-ac-only")->enabled)
8b5746bc
         cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
ab0d2f05
 
2accc66f
     if(optget(opts, "dev-ac-depth")->enabled)
8b5746bc
         cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, optget(opts, "dev-ac-depth")->numarg);
ab0d2f05
 
2accc66f
     if(optget(opts, "leave-temps")->enabled)
8b5746bc
         cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
33068e09
 
3cab931d
     if(optget(opts, "force-to-disk")->enabled)
8b5746bc
         cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
3cab931d
 
62315ce6
     if(optget(opts, "bytecode-unsigned")->enabled)
8b5746bc
         dboptions |= CL_DB_BYTECODE_UNSIGNED;
62315ce6
 
b63681a5
     if((opt = optget(opts,"bytecode-timeout"))->enabled)
8b5746bc
         cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
 
5eaf0b32
     if (optget(opts, "nocerts")->enabled)
         cl_engine_set_num(engine, CL_ENGINE_DISABLE_PE_CERTS, 1);
 
     if (optget(opts, "dumpcerts")->enabled)
         cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1);
 
9f3afdb8
     if((opt = optget(opts,"bytecode-mode"))->enabled) {
8b5746bc
         enum bytecode_mode mode;
 
         if (!strcmp(opt->strarg, "ForceJIT"))
             mode = CL_BYTECODE_MODE_JIT;
         else if(!strcmp(opt->strarg, "ForceInterpreter"))
             mode = CL_BYTECODE_MODE_INTERPRETER;
         else if(!strcmp(opt->strarg, "Test"))
             mode = CL_BYTECODE_MODE_TEST;
         else
             mode = CL_BYTECODE_MODE_AUTO;
 
         cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
9f3afdb8
     }
aa745db7
 
99e22630
     if((opt = optget(opts, "statistics"))->enabled) {
 	while(opt) {
 	    if (!strcasecmp(opt->strarg, "bytecode")) {
 		dboptions |= CL_DB_BYTECODE_STATS;
 	    }
5c2c7233
 	    else if (!strcasecmp(opt->strarg, "pcre")) {
 		dboptions |= CL_DB_PCRE_STATS;
 	    }
99e22630
 	    opt = opt->nextarg;
         }
     }
 
68bf8dd8
     /* JSON check to prevent engine loading if specified without libjson-c  */
 #if HAVE_JSON
     if (optget(opts, "gen-json")->enabled)
d7979d4f
         options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
68bf8dd8
 #else
     if (optget(opts, "gen-json")->enabled) {
f0ede479
         logg("!Can't generate json (gen-json). libjson-c dev library was missing or misconfigured when ClamAV was built.\n");
68bf8dd8
 
         cl_engine_free(engine);
         return 2;
     }
 #endif
 
269d520d
     if((opt = optget(opts, "tempdir"))->enabled) {
8b5746bc
         if((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
             logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
33068e09
     }
 
8daa97de
     if((opt = optget(opts, "database"))->active) {
8b5746bc
         while(opt) {
             if((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
                 logg("!%s\n", cl_strerror(ret));
 
                 cl_engine_free(engine);
                 return 2;
             }
 
             opt = opt->nextarg;
         }
e3aaff8e
     } else {
8b5746bc
         char *dbdir = freshdbdir();
 
         if((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
             logg("!%s\n", cl_strerror(ret));
 
             free(dbdir);
             cl_engine_free(engine);
             return 2;
         }
 
         free(dbdir);
e3aaff8e
     }
 
e492c653
     /* pcre engine limits - required for cl_engine_compile */
     if ((opt = optget(opts, "pcre-match-limit"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_PCRE_MATCH_LIMIT) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 2;
         }
     }
 
     if ((opt = optget(opts, "pcre-recmatch-limit"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_PCRE_RECMATCH_LIMIT) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 2;
         }
     }
 
370892d0
     if((ret = cl_engine_compile(engine)) != 0) {
8b5746bc
         logg("!Database initialization error: %s\n", cl_strerror(ret));;
 
         cl_engine_free(engine);
         return 2;
2d70a403
     }
e3aaff8e
 
248c2f4d
     if(optget(opts, "archive-verbose")->enabled) {
8b5746bc
         cl_engine_set_clcb_meta(engine, meta);
         cl_engine_set_clcb_pre_cache(engine, pre);
         cl_engine_set_clcb_post_scan(engine, post);
248c2f4d
     }
 
7a2997f1
     /* set limits */
e3aaff8e
 
09dc3ecb
     if((opt = optget(opts, "max-scansize"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
370892d0
     }
281c7642
 
09dc3ecb
     if((opt = optget(opts, "max-filesize"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
370892d0
     }
e3aaff8e
 
4cd80898
 #ifndef _WIN32
9a03413e
     if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
8b5746bc
         if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
             logg("^System limit for file size is lower than engine->maxfilesize\n");
         if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
             logg("^System limit for file size is lower than engine->maxscansize\n");
9a03413e
     } else {
8b5746bc
         logg("^Cannot obtain resource limits for file size\n");
9a03413e
     }
 #endif
 
09dc3ecb
     if((opt = optget(opts, "max-files"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
370892d0
     }
e3aaff8e
 
09dc3ecb
     if((opt = optget(opts, "max-recursion"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
370892d0
     }
e3aaff8e
 
b2726a53
     /* Engine max sizes */
 
     if((opt = optget(opts, "max-embeddedpe"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
b2726a53
     }
 
     if((opt = optget(opts, "max-htmlnormalize"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
b2726a53
     }
 
     if((opt = optget(opts, "max-htmlnotags"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
b2726a53
     }
 
     if((opt = optget(opts, "max-scriptnormalize"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
b2726a53
     }
 
     if((opt = optget(opts, "max-ziptypercg"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
b2726a53
     }
 
97fbb02b
     if((opt = optget(opts, "max-partitions"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_PARTITIONS) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
97fbb02b
     }
 
067bce5f
     if((opt = optget(opts, "max-iconspe"))->active) {
8b5746bc
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_ICONSPE) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
067bce5f
     }
 
731c8e62
     if((opt = optget(opts, "max-rechwp3"))->active) {
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECHWP3, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_MAX_RECHWP3) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
         }
     }
 
49b33289
     if ((opt = optget(opts, "timelimit"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_TIME_LIMIT, opt->numarg))) {
8b5746bc
             logg("!cli_engine_set_num(CL_ENGINE_TIME_LIMIT) failed: %s\n", cl_strerror(ret));
 
             cl_engine_free(engine);
             return 2;
49b33289
         }
     }
 
37415732
     if ((opt = optget(opts, "pcre-max-filesize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
             logg("!cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 2;
         }
     }
 
269d520d
     /* set scan options */
1f1bf36b
     if(optget(opts, "allmatch")->enabled) {
d7979d4f
         options.general |= CL_SCAN_GENERAL_ALLMATCHES;
1f1bf36b
     }
6ad45a29
 
f61e92da
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts,"phishing-ssl")->enabled) ||
         (optget(opts,"alert-phishing-ssl")->enabled))
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
6ef42bc3
 
f61e92da
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts,"phishing-cloak")->enabled) ||
         (optget(opts,"alert-phishing-cloak")->enabled))
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
269d520d
 
f61e92da
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts,"partition-intersection")->enabled) ||
         (optget(opts,"alert-partition-intersection")->enabled))
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_PARTITION_INTXN;
97fbb02b
 
269d520d
     if(optget(opts,"heuristic-scan-precedence")->enabled)
d7979d4f
         options.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
269d520d
 
     if(optget(opts, "scan-archive")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_ARCHIVE;
6ef42bc3
 
f61e92da
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "detect-broken")->enabled) || 
         (optget(opts, "alert-broken")->enabled)) {
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
f61e92da
     }
 
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "block-encrypted")->enabled) ||
         (optget(opts, "alert-encrypted")->enabled)) {
         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
     }
6ef42bc3
 
f61e92da
     if (optget(opts, "alert-encrypted-archive")->enabled)
         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
6ef42bc3
 
f61e92da
     if (optget(opts, "alert-encrypted-doc")->enabled)
         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
 
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "block-macros")->enabled) ||
         (optget(opts, "alert-macros")->enabled)) {
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_MACROS;
f61e92da
     }
c1836324
 
269d520d
     if(optget(opts, "scan-pe")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_PE;
6ef42bc3
 
269d520d
     if(optget(opts, "scan-elf")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_ELF;
3f97a1e7
 
269d520d
     if(optget(opts, "scan-ole2")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_OLE2;
6ef42bc3
 
269d520d
     if(optget(opts, "scan-pdf")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_PDF;
c5107e70
 
1e41fdba
     if(optget(opts, "scan-swf")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_SWF;
1e41fdba
 
ce6becd5
     if(optget(opts, "scan-html")->enabled && optget(opts, "normalize")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_HTML;
6ef42bc3
 
6a4dd9dc
     if(optget(opts, "scan-mail")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_MAIL;
6ef42bc3
 
ea9ffd29
     if(optget(opts, "scan-xmldocs")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_XMLDOCS;
ea9ffd29
 
     if(optget(opts, "scan-hwp3")->enabled)
d7979d4f
         options.parse |= CL_SCAN_PARSE_HWP3;
ea9ffd29
 
78606d72
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "algorithmic-detection")->enabled) && /* && used due to default-yes for both options */
         (optget(opts, "heuristic-alerts")->enabled)) {
d7979d4f
         options.general |= CL_SCAN_GENERAL_HEURISTICS;
78606d72
     }
1b661cef
 
f61e92da
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "block-max")->enabled) || 
         (optget(opts, "alert-exceeds-max")->enabled)) {
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
312b7e53
     }
 
3d7547cf
 #ifdef HAVE__INTERNAL__SHA_COLLECT
     if(optget(opts, "dev-collect-hashes")->enabled)
d7979d4f
         options.dev |= CL_SCAN_DEV_COLLECT_SHA;
3d7547cf
 #endif
 
63feb6cd
     if(optget(opts, "dev-performance")->enabled)
cca445ee
         options.dev |= CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO;
63feb6cd
 
269d520d
     if(optget(opts, "detect-structured")->enabled) {
d7979d4f
         options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
8b5746bc
 
         if((opt = optget(opts, "structured-ssn-format"))->enabled) {
             switch(opt->numarg) {
             case 0:
d7979d4f
                 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
8b5746bc
                 break;
             case 1:
d7979d4f
                 options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
8b5746bc
                 break;
             case 2:
d7979d4f
                 options.heuristic |= (CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL | CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED);
8b5746bc
                 break;
             default:
                 logg("!Invalid argument for --structured-ssn-format\n");
                 return 2;
             }
         } else {
d7979d4f
             options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
8b5746bc
         }
5fe6e72b
 
8b5746bc
         if((opt = optget(opts, "structured-ssn-count"))->active) {
             if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
                 logg("!cli_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
 
                 cl_engine_free(engine);
                 return 2;
             }
         }
 
         if((opt = optget(opts, "structured-cc-count"))->active) {
             if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
                 logg("!cli_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
                 cl_engine_free(engine);
                 return 2;
             }
         }
269d520d
     } else {
d7979d4f
         options.heuristic &= ~CL_SCAN_HEURISTIC_STRUCTURED;
269d520d
     }
a6e38800
 
e3aaff8e
 #ifdef C_LINUX
d9b55a82
     procdev = (dev_t) 0;
d9b6b8c7
     if(CLAMSTAT("/proc", &sb) != -1 && !sb.st_size)
8b5746bc
         procdev = sb.st_dev;
e3aaff8e
 #endif
 
     /* check filetype */
c2b6681b
     if(!opts->filename && !optget(opts, "file-list")->enabled) {
8b5746bc
         /* we need full path for some reasons (eg. archive handling) */
         if(!getcwd(cwd, sizeof(cwd))) {
             logg("!Can't get absolute pathname of current working directory\n");
             ret = 2;
         } else {
             CLAMSTAT(cwd, &sb);
d7979d4f
             scandirs(cwd, engine, opts, &options, 1, sb.st_dev);
8b5746bc
         }
2d70a403
 
c2b6681b
     } else if(opts->filename && !optget(opts, "file-list")->enabled && !strcmp(opts->filename[0], "-")) { /* read data from stdin */
d7979d4f
         ret = scanstdin(engine, opts, &options);
e3aaff8e
     } else {
8b5746bc
         if(opts->filename && optget(opts, "file-list")->enabled)
             logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
 
         while((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
             if(LSTAT(file, &sb) == -1) {
                 perror(file);
                 logg("^%s: Can't access file\n", file);
                 ret = 2;
             } else {
                 for(i = strlen(file) - 1; i > 0; i--) {
                     if(file[i] == *PATHSEP)
                         file[i] = 0;
                     else
                         break;
                 }
 
                 if(S_ISLNK(sb.st_mode)) {
                     if(dirlnk == 0 && filelnk == 0) {
                         if(!printinfected)
                             logg("%s: Symbolic link\n", file);
                     } else if(CLAMSTAT(file, &sb) != -1) {
                         if(S_ISREG(sb.st_mode) && filelnk) {
d7979d4f
                             scanfile(file, engine, opts, &options);
8b5746bc
                         } else if(S_ISDIR(sb.st_mode) && dirlnk) {
d7979d4f
                             scandirs(file, engine, opts, &options, 1, sb.st_dev);
8b5746bc
                         } else {
                             if(!printinfected)
                                 logg("%s: Symbolic link\n", file);
                         }
                     }
                 } else if(S_ISREG(sb.st_mode)) {
d7979d4f
                     scanfile(file, engine, opts, &options);
8b5746bc
                 } else if(S_ISDIR(sb.st_mode)) {
d7979d4f
                     scandirs(file, engine, opts, &options, 1, sb.st_dev);
8b5746bc
                 } else {
                     logg("^%s: Not supported file type\n", file);
                     ret = 2;
                 }
             }
 
             free(file);
         }
e3aaff8e
     }
 
99e22630
     if((opt = optget(opts, "statistics"))->enabled) {
 	while(opt) {
 	    if (!strcasecmp(opt->strarg, "bytecode")) {
 		cli_sigperf_print();
 		cli_sigperf_events_destroy();
 	    }
5c2c7233
 #if HAVE_PCRE
 	    else if (!strcasecmp(opt->strarg, "pcre")) {
 		cli_pcre_perf_print();
 		cli_pcre_perf_events_destroy();
 	    }
 #endif
99e22630
 	    opt = opt->nextarg;
         }
54402320
     }
 
7a2997f1
     /* free the engine */
370892d0
     cl_engine_free(engine);
e3aaff8e
 
8770404a
     /* overwrite return code - infection takes priority */
7a2997f1
     if(info.ifiles)
8b5746bc
         ret = 1;
8770404a
     else if(info.errors)
8b5746bc
         ret = 2;
e3aaff8e
 
     return ret;
 }
c4cd560b
 
 int is_valid_hostid(void)
 {
     int count, i;
 
     if (strlen(hostid) != 36)
         return 0;
 
     count=0;
     for (i=0; i < 36; i++)
         if (hostid[i] == '-')
             count++;
 
     if (count != 4)
         return 0;
 
     if (hostid[8] != '-' || hostid[13] != '-' || hostid[18] != '-' || hostid[23] != '-')
         return 0;
 
     return 1;
 }
 
 char *get_hostid(void *cbdata)
 {
6df13d04
     UNUSEDPARAM(cbdata);
 
c4cd560b
     if (!strcmp(hostid, "none"))
         return NULL;
 
     if (!is_valid_hostid())
         return strdup(STATS_ANON_UUID);
 
     logg("HostID is valid: %s\n", hostid);
 
     return strdup(hostid);
 }