2
* Copyright (C) 2002 - 2006 Tomasz Kojm <tkojm@clamav.net>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 2 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
#include "clamav-config.h"
32
#include <sys/types.h>
45
#if defined(USE_SYSLOG) && !defined(C_AIX)
51
#include "freshclamcodes.h"
53
#include "libclamav/others.h"
54
#include "libclamav/str.h"
56
#include "shared/optparser.h"
57
#include "shared/output.h"
58
#include "shared/misc.h"
64
static short terminate = 0;
65
extern int active_children;
67
static short foreground = 1;
68
char updtmpdir[512], dbdir[512];
70
const char *pidfile = NULL;
73
void submit_host_info(struct optstruct *opts);
74
char *get_hostid(void *cbdata);
75
int is_valid_hostid(void);
86
waitpid (-1, NULL, WNOHANG);
93
/* no action, app will get EPIPE */
116
cli_rmdirs (updtmpdir);
119
logg ("Update process terminated\n");
127
writepid (const char *pidfile)
131
old_umask = umask (0006);
132
if ((fd = fopen (pidfile, "w")) == NULL)
134
logg ("!Can't save PID to file %s: %s\n", pidfile, strerror (errno));
138
fprintf (fd, "%d\n", (int) getpid ());
150
mprintf (" Clam AntiVirus: freshclam %s\n",
152
printf (" By The ClamAV Team: http://www.clamav.net/about.html#credits\n");
153
printf (" (C) 2007-2009 Sourcefire, Inc. et al.\n\n");
155
mprintf (" --help -h show help\n");
157
(" --version -V print version number and exit\n");
158
mprintf (" --verbose -v be verbose\n");
160
(" --debug enable debug messages\n");
162
(" --quiet only output error messages\n");
164
(" --no-warnings don't print and log warnings\n");
166
(" --stdout write to stdout instead of stderr\n");
169
(" --config-file=FILE read configuration from FILE.\n");
170
mprintf (" --log=FILE -l FILE log into FILE\n");
172
mprintf (" --daemon -d run in daemon mode\n");
174
(" --pid=FILE -p FILE save daemon's pid in FILE\n");
175
mprintf (" --user=USER -u USER run as USER\n");
178
(" --no-dns force old non-DNS verification method\n");
180
(" --checks=#n -c #n number of checks per day, 1 <= n <= 50\n");
182
(" --datadir=DIRECTORY download new databases into DIRECTORY\n");
185
(" --daemon-notify[=/path/clamd.conf] send RELOAD command to clamd\n");
188
(" --local-address=IP -a IP bind to IP for HTTP downloads\n");
190
(" --on-update-execute=COMMAND execute COMMAND after successful update\n");
192
(" --on-error-execute=COMMAND execute COMMAND if errors occured\n");
194
(" --on-outdated-execute=COMMAND execute COMMAND when software is outdated\n");
196
(" --list-mirrors print mirrors from mirrors.dat\n");
198
(" --enable-stats enable statistical information reporting\n");
200
(" --stats-host-id=UUID HostID in the form of an UUID to use when submitting statistical information\n");
202
(" --update-db=DBNAME only update database DBNAME\n");
208
download (const struct optstruct *opts, const char *cfgfile)
210
int ret = 0, try = 1, maxattempts = 0;
211
const struct optstruct *opt;
214
maxattempts = optget (opts, "MaxAttempts")->numarg;
215
logg ("*Max retries == %d\n", maxattempts);
217
if (!(opt = optget (opts, "DatabaseMirror"))->enabled)
219
logg ("^You must specify at least one database mirror in %s\n",
227
ret = downloadmanager (opts, opt->strarg, try);
231
if (ret == FCE_CONNECTION || ret == FCE_BADCVD
232
|| ret == FCE_FAILEDGET || ret == FCE_MIRRORNOTSYNC)
234
if (try < maxattempts)
236
logg ("Trying again in 5 secs...\n");
243
logg ("Giving up on %s...\n", opt->strarg);
244
opt = (struct optstruct *) opt->nextarg;
247
logg ("Update failed. Your network may be down or none of the mirrors listed in %s is working. Check http://www.clamav.net/documentation.html for possible reasons.\n", cfgfile);
263
msg_callback (enum cl_msg severity, const char *fullmsg, const char *msg,
266
UNUSEDPARAM(fullmsg);
272
logg ("^[LibClamAV] %s", msg);
275
logg ("~[LibClamAV] %s", msg);
278
logg ("*[LibClamAV] %s", msg);
284
main (int argc, char **argv)
286
int ret = FCE_CONNECTION, retcl;
287
const char *cfgfile, *arg = NULL;
289
struct optstruct *opts;
290
const struct optstruct *opt;
292
struct sigaction sigact;
293
struct sigaction oldact;
305
if ((retcl = cl_init (CL_INIT_DEFAULT)))
307
mprintf ("!Can't initialize libclamav: %s\n", cl_strerror (retcl));
312
optparse (NULL, argc, argv, 1, OPT_FRESHCLAM, 0, NULL)) == NULL)
314
mprintf ("!Can't parse command line options\n");
318
if (optget (opts, "help")->enabled)
325
/* parse the config file */
326
cfgfile = optget (opts, "config-file")->strarg;
327
pt = strdup (cfgfile);
329
optparse (cfgfile, 0, NULL, 1, OPT_FRESHCLAM, 0, opts)) == NULL)
331
fprintf (stderr, "ERROR: Can't open/parse the config file %s\n", pt);
337
if (optget (opts, "version")->enabled)
339
print_version (optget (opts, "DatabaseDirectory")->strarg);
344
/* Stats/intelligence gathering */
345
if (optget(opts, "stats-host-id")->enabled) {
346
char *p = optget(opts, "stats-host-id")->strarg;
348
if (strcmp(p, "default")) {
349
if (!strcmp(p, "anonymous")) {
350
strcpy(hostid, STATS_ANON_UUID);
352
if (strlen(p) > 36) {
353
logg("!Invalid HostID\n");
361
strcpy(hostid, "default");
364
submit_host_info(opts);
366
if (optget (opts, "HTTPProxyPassword")->enabled)
368
if (CLAMSTAT (cfgfile, &statbuf) == -1)
370
logg ("^Can't stat %s (critical error)\n", cfgfile);
377
st_mode & (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH |
380
logg ("^Insecure permissions (for HTTPProxyPassword): %s must have no more than 0700 permissions.\n", cfgfile);
388
/* freshclam shouldn't work with root privileges */
389
dbowner = optget (opts, "DatabaseOwner")->strarg;
393
if ((user = getpwnam (dbowner)) == NULL)
395
logg ("^Can't get information about user %s.\n", dbowner);
400
if (optget (opts, "AllowSupplementaryGroups")->enabled)
402
#ifdef HAVE_INITGROUPS
403
if (initgroups (dbowner, user->pw_gid))
405
logg ("^initgroups() failed.\n");
407
return FCE_USERORGROUP;
413
#ifdef HAVE_SETGROUPS
414
if (setgroups (1, &user->pw_gid))
416
logg ("^setgroups() failed.\n");
418
return FCE_USERORGROUP;
423
if (setgid (user->pw_gid))
425
logg ("^setgid(%d) failed.\n", (int) user->pw_gid);
427
return FCE_USERORGROUP;
430
if (setuid (user->pw_uid))
432
logg ("^setuid(%d) failed.\n", (int) user->pw_uid);
434
return FCE_USERORGROUP;
437
#endif /* HAVE_PWD_H */
439
/* initialize some important variables */
441
if (optget (opts, "Debug")->enabled || optget (opts, "debug")->enabled)
444
if (optget (opts, "verbose")->enabled)
447
if (optget (opts, "quiet")->enabled)
450
if (optget (opts, "no-warnings")->enabled)
456
if (optget (opts, "stdout")->enabled)
459
/* initialize logger */
460
logg_verbose = mprintf_verbose ? 1 : optget (opts, "LogVerbose")->enabled;
461
logg_time = optget (opts, "LogTime")->enabled;
462
logg_size = optget (opts, "LogFileMaxSize")->numarg;
464
logg_rotate = optget(opts, "LogRotate")->enabled;
466
if ((opt = optget (opts, "UpdateLogFile"))->enabled)
468
logg_file = opt->strarg;
469
if (logg ("#--------------------------------------\n"))
471
mprintf ("!Problem with internal logger (UpdateLogFile = %s).\n",
480
#if defined(USE_SYSLOG) && !defined(C_AIX)
481
if (optget (opts, "LogSyslog")->enabled)
483
int fac = LOG_LOCAL6;
485
if ((opt = optget (opts, "LogFacility"))->enabled)
487
if ((fac = logg_facility (opt->strarg)) == -1)
489
mprintf ("!LogFacility: %s: No such facility.\n",
496
openlog ("freshclam", LOG_PID, fac);
501
cl_set_clcb_msg (msg_callback);
502
/* change the current working directory */
503
if (chdir (optget (opts, "DatabaseDirectory")->strarg))
505
logg ("!Can't change dir to %s\n",
506
optget (opts, "DatabaseDirectory")->strarg);
508
return FCE_DIRECTORY;
512
if (!getcwd (dbdir, sizeof (dbdir)))
514
logg ("!getcwd() failed\n");
516
return FCE_DIRECTORY;
518
logg ("*Current working dir is %s\n", dbdir);
522
if (optget (opts, "list-mirrors")->enabled)
524
if (mirman_read ("mirrors.dat", &mdat, 1) == -1)
526
printf ("Can't read mirrors.dat\n");
536
if ((opt = optget (opts, "PrivateMirror"))->enabled)
538
struct optstruct *dbm, *opth;
540
dbm = (struct optstruct *) optget (opts, "DatabaseMirror");
541
dbm->active = dbm->enabled = 1;
544
if (cli_strbcasestr (opt->strarg, ".clamav.net"))
546
logg ("!PrivateMirror: *.clamav.net is not allowed in this mode\n");
548
return FCE_PRIVATEMIRROR;
553
dbm->strarg = strdup (opt->strarg);
556
logg ("!strdup() failed\n");
563
(struct optstruct *) calloc (1,
564
sizeof (struct optstruct));
567
logg ("!calloc() failed\n");
575
while ((opt = opt->nextarg));
577
opth->nextarg = NULL;
588
/* disable DNS db checks */
589
opth = (struct optstruct *) optget (opts, "no-dns");
590
opth->active = opth->enabled = 1;
592
/* disable scripted updates */
593
opth = (struct optstruct *) optget (opts, "ScriptedUpdates");
594
opth->active = opth->enabled = 0;
600
signal (SIGINT, sighandler);
602
memset (&sigact, 0, sizeof (struct sigaction));
603
sigact.sa_handler = sighandler;
604
sigaction (SIGINT, &sigact, NULL);
605
sigaction (SIGPIPE, &sigact, NULL);
607
if (optget (opts, "daemon")->enabled)
609
int bigsleep, checks;
613
sigaction (SIGTERM, &sigact, NULL);
614
sigaction (SIGHUP, &sigact, NULL);
615
sigaction (SIGCHLD, &sigact, NULL);
618
checks = optget (opts, "Checks")->numarg;
622
logg ("^Number of checks must be a positive integer.\n");
627
if (!optget (opts, "DNSDatabaseInfo")->enabled
628
|| optget (opts, "no-dns")->enabled)
632
logg ("^Number of checks must be between 1 and 50.\n");
638
bigsleep = 24 * 3600 / checks;
641
if (!optget (opts, "Foreground")->enabled)
643
if (daemonize () == -1)
645
logg ("!daemonize() failed\n");
647
return FCE_FAILEDUPDATE;
650
mprintf_disabled = 1;
654
if ((opt = optget (opts, "PidFile"))->enabled)
656
pidfile = opt->strarg;
662
logg ("#freshclam daemon %s (OS: " TARGET_OS_TYPE ", ARCH: "
663
TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ")\n",
668
ret = download (opts, cfgfile);
672
if ((opt = optget (opts, "OnErrorExecute"))->enabled)
676
execute ("OnErrorExecute", arg, opts);
681
logg ("#--------------------------------------\n");
683
sigaction (SIGALRM, &sigact, &oldact);
686
sigaction (SIGUSR1, &sigact, &oldact);
700
while (!terminate && now < wakeup);
704
logg ("Received signal: wake up\n");
707
else if (terminate == -2)
709
logg ("Received signal: re-opening log file\n");
716
sigaction (SIGALRM, &oldact, NULL);
719
sigaction (SIGUSR1, &oldact, NULL);
726
ret = download (opts, cfgfile);
731
if ((opt = optget (opts, "OnErrorExecute"))->enabled)
732
execute ("OnErrorExecute", opt->strarg, opts);
747
void submit_host_info(struct optstruct *opts)
749
struct cl_engine *engine;
752
if (!optget(opts, "enable-stats")->enabled)
755
engine = cl_engine_new();
759
if (optget (opts, "Debug")->enabled || optget (opts, "debug")->enabled)
762
if (optget (opts, "verbose")->enabled)
765
cl_engine_stats_enable(engine);
767
intel = engine->stats_data;
769
engine->cb_stats_submit = NULL;
770
cl_engine_free(engine);
774
intel->host_info = calloc(1, strlen(TARGET_OS_TYPE) + strlen(TARGET_ARCH_TYPE) + 2);
775
if (!(intel->host_info)) {
776
engine->cb_stats_submit = NULL;
777
cl_engine_free(engine);
781
sprintf(intel->host_info, "%s %s", TARGET_OS_TYPE, TARGET_ARCH_TYPE);
783
if (!strcmp(hostid, "none"))
784
cl_engine_set_clcb_stats_get_hostid(engine, NULL);
785
else if (strcmp(hostid, "default"))
786
cl_engine_set_clcb_stats_get_hostid(engine, get_hostid);
788
if (optget(opts, "stats-timeout")->enabled) {
789
cl_engine_set_num(engine, CL_ENGINE_STATS_TIMEOUT, optget(opts, "StatsTimeout")->numarg);
792
cl_engine_free(engine);
795
int is_valid_hostid(void)
799
if (strlen(hostid) != 36)
803
for (i=0; i < 36; i++)
804
if (hostid[i] == '-')
810
if (hostid[8] != '-' || hostid[13] != '-' || hostid[18] != '-' || hostid[23] != '-')
816
char *get_hostid(void *cbdata)
820
if (!strcmp(hostid, "none"))
823
if (!is_valid_hostid())
824
return strdup(STATS_ANON_UUID);
826
logg("HostID is valid: %s\n", hostid);
828
return strdup(hostid);