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)
49
#include <openssl/ssl.h>
50
#include <openssl/err.h>
51
#include "libclamav/crypto.h"
55
#include "freshclamcodes.h"
57
#include "libclamav/others.h"
58
#include "libclamav/str.h"
60
#include "shared/optparser.h"
61
#include "shared/output.h"
62
#include "shared/misc.h"
68
static short terminate = 0;
69
extern int active_children;
71
static short foreground = 1;
72
char updtmpdir[512], dbdir[512];
74
const char *pidfile = NULL;
77
void submit_host_info(struct optstruct *opts);
78
char *get_hostid(void *cbdata);
89
waitpid (-1, NULL, WNOHANG);
96
/* no action, app will get EPIPE */
119
cli_rmdirs (updtmpdir);
122
logg ("Update process terminated\n");
130
writepid (const char *pidfile)
134
old_umask = umask (0006);
135
if ((fd = fopen (pidfile, "w")) == NULL)
137
logg ("!Can't save PID to file %s: %s\n", pidfile, strerror (errno));
141
fprintf (fd, "%d\n", (int) getpid ());
153
mprintf (" Clam AntiVirus: freshclam %s\n",
155
printf (" By The ClamAV Team: http://www.clamav.net/team\n");
156
printf (" (C) 2007-2009 Sourcefire, Inc. et al.\n\n");
158
mprintf (" --help -h show help\n");
160
(" --version -V print version number and exit\n");
161
mprintf (" --verbose -v be verbose\n");
163
(" --debug enable debug messages\n");
165
(" --quiet only output error messages\n");
167
(" --no-warnings don't print and log warnings\n");
169
(" --stdout write to stdout instead of stderr\n");
172
(" --config-file=FILE read configuration from FILE.\n");
173
mprintf (" --log=FILE -l FILE log into FILE\n");
175
mprintf (" --daemon -d run in daemon mode\n");
177
(" --pid=FILE -p FILE save daemon's pid in FILE\n");
178
mprintf (" --user=USER -u USER run as USER\n");
181
(" --no-dns force old non-DNS verification method\n");
183
(" --checks=#n -c #n number of checks per day, 1 <= n <= 50\n");
185
(" --datadir=DIRECTORY download new databases into DIRECTORY\n");
188
(" --daemon-notify[=/path/clamd.conf] send RELOAD command to clamd\n");
191
(" --local-address=IP -a IP bind to IP for HTTP downloads\n");
193
(" --on-update-execute=COMMAND execute COMMAND after successful update\n");
195
(" --on-error-execute=COMMAND execute COMMAND if errors occured\n");
197
(" --on-outdated-execute=COMMAND execute COMMAND when software is outdated\n");
199
(" --list-mirrors print mirrors from mirrors.dat\n");
201
(" --enable-stats enable statistical information reporting\n");
203
(" --stats-host-id=UUID HostID in the form of an UUID to use when submitting statistical information\n");
205
(" --update-db=DBNAME only update database DBNAME\n");
211
download (const struct optstruct *opts, const char *cfgfile)
213
int ret = 0, try = 1, maxattempts = 0;
214
const struct optstruct *opt;
217
maxattempts = optget (opts, "MaxAttempts")->numarg;
218
logg ("*Max retries == %d\n", maxattempts);
220
if (!(opt = optget (opts, "DatabaseMirror"))->enabled)
222
logg ("^You must specify at least one database mirror in %s\n",
230
ret = downloadmanager (opts, opt->strarg, try);
234
if (ret == FCE_CONNECTION || ret == FCE_BADCVD
235
|| ret == FCE_FAILEDGET || ret == FCE_MIRRORNOTSYNC)
237
if (try < maxattempts)
239
logg ("Trying again in 5 secs...\n");
246
logg ("Giving up on %s...\n", opt->strarg);
247
opt = (struct optstruct *) opt->nextarg;
250
logg ("Update failed. Your network may be down or none of the mirrors listed in %s is working. Check http://www.clamav.net/support/mirror-problem for possible reasons.\n", cfgfile);
266
msg_callback (enum cl_msg severity, const char *fullmsg, const char *msg,
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
cl_initialize_crypto();
307
if ((retcl = cl_init (CL_INIT_DEFAULT)))
309
mprintf ("!Can't initialize libclamav: %s\n", cl_strerror (retcl));
314
optparse (NULL, argc, argv, 1, OPT_FRESHCLAM, 0, NULL)) == NULL)
316
mprintf ("!Can't parse command line options\n");
320
if (optget (opts, "help")->enabled)
327
/* parse the config file */
328
cfgfile = optget (opts, "config-file")->strarg;
329
pt = strdup (cfgfile);
331
optparse (cfgfile, 0, NULL, 1, OPT_FRESHCLAM, 0, opts)) == NULL)
333
fprintf (stderr, "ERROR: Can't open/parse the config file %s\n", pt);
339
if (optget (opts, "version")->enabled)
341
print_version (optget (opts, "DatabaseDirectory")->strarg);
346
/* Stats/intelligence gathering */
347
if (optget(opts, "stats-host-id")->enabled) {
348
char *p = optget(opts, "stats-host-id")->strarg;
350
if (strcmp(p, "default")) {
351
if (!strcmp(p, "anonymous")) {
352
strcpy(hostid, STATS_ANON_UUID);
354
if (strlen(p) > 36) {
355
logg("!Invalid HostID\n");
363
strcpy(hostid, "default");
366
submit_host_info(opts);
368
if (optget (opts, "HTTPProxyPassword")->enabled)
370
if (CLAMSTAT (cfgfile, &statbuf) == -1)
372
logg ("^Can't stat %s (critical error)\n", cfgfile);
379
st_mode & (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH |
382
logg ("^Insecure permissions (for HTTPProxyPassword): %s must have no more than 0700 permissions.\n", cfgfile);
390
/* freshclam shouldn't work with root privileges */
391
dbowner = optget (opts, "DatabaseOwner")->strarg;
395
if ((user = getpwnam (dbowner)) == NULL)
397
logg ("^Can't get information about user %s.\n", dbowner);
402
if (optget (opts, "AllowSupplementaryGroups")->enabled)
404
#ifdef HAVE_INITGROUPS
405
if (initgroups (dbowner, user->pw_gid))
407
logg ("^initgroups() failed.\n");
409
return FCE_USERORGROUP;
415
#ifdef HAVE_SETGROUPS
416
if (setgroups (1, &user->pw_gid))
418
logg ("^setgroups() failed.\n");
420
return FCE_USERORGROUP;
425
if (setgid (user->pw_gid))
427
logg ("^setgid(%d) failed.\n", (int) user->pw_gid);
429
return FCE_USERORGROUP;
432
if (setuid (user->pw_uid))
434
logg ("^setuid(%d) failed.\n", (int) user->pw_uid);
436
return FCE_USERORGROUP;
439
#endif /* HAVE_PWD_H */
441
/* initialize some important variables */
443
if (optget (opts, "Debug")->enabled || optget (opts, "debug")->enabled)
446
if (optget (opts, "verbose")->enabled)
449
if (optget (opts, "quiet")->enabled)
452
if (optget (opts, "no-warnings")->enabled)
458
if (optget (opts, "stdout")->enabled)
461
/* initialize logger */
462
logg_verbose = mprintf_verbose ? 1 : optget (opts, "LogVerbose")->enabled;
463
logg_time = optget (opts, "LogTime")->enabled;
464
logg_size = optget (opts, "LogFileMaxSize")->numarg;
466
logg_rotate = optget(opts, "LogRotate")->enabled;
468
if ((opt = optget (opts, "UpdateLogFile"))->enabled)
470
logg_file = opt->strarg;
471
if (logg ("#--------------------------------------\n"))
473
mprintf ("!Problem with internal logger (UpdateLogFile = %s).\n",
482
#if defined(USE_SYSLOG) && !defined(C_AIX)
483
if (optget (opts, "LogSyslog")->enabled)
485
int fac = LOG_LOCAL6;
487
if ((opt = optget (opts, "LogFacility"))->enabled)
489
if ((fac = logg_facility (opt->strarg)) == -1)
491
mprintf ("!LogFacility: %s: No such facility.\n",
498
openlog ("freshclam", LOG_PID, fac);
503
cl_set_clcb_msg (msg_callback);
504
/* change the current working directory */
505
if (chdir (optget (opts, "DatabaseDirectory")->strarg))
507
logg ("!Can't change dir to %s\n",
508
optget (opts, "DatabaseDirectory")->strarg);
510
return FCE_DIRECTORY;
514
if (!getcwd (dbdir, sizeof (dbdir)))
516
logg ("!getcwd() failed\n");
518
return FCE_DIRECTORY;
520
logg ("*Current working dir is %s\n", dbdir);
524
if (optget (opts, "list-mirrors")->enabled)
526
if (mirman_read ("mirrors.dat", &mdat, 1) == -1)
528
printf ("Can't read mirrors.dat\n");
538
if ((opt = optget (opts, "PrivateMirror"))->enabled)
540
struct optstruct *dbm, *opth;
542
dbm = (struct optstruct *) optget (opts, "DatabaseMirror");
543
dbm->active = dbm->enabled = 1;
546
if (cli_strbcasestr (opt->strarg, ".clamav.net"))
548
logg ("!PrivateMirror: *.clamav.net is not allowed in this mode\n");
550
return FCE_PRIVATEMIRROR;
555
dbm->strarg = strdup (opt->strarg);
558
logg ("!strdup() failed\n");
565
(struct optstruct *) calloc (1,
566
sizeof (struct optstruct));
569
logg ("!calloc() failed\n");
577
while ((opt = opt->nextarg));
579
opth->nextarg = NULL;
590
/* disable DNS db checks */
591
opth = (struct optstruct *) optget (opts, "no-dns");
592
opth->active = opth->enabled = 1;
594
/* disable scripted updates */
595
opth = (struct optstruct *) optget (opts, "ScriptedUpdates");
596
opth->active = opth->enabled = 0;
602
signal (SIGINT, sighandler);
604
memset (&sigact, 0, sizeof (struct sigaction));
605
sigact.sa_handler = sighandler;
606
sigaction (SIGINT, &sigact, NULL);
607
sigaction (SIGPIPE, &sigact, NULL);
609
if (optget (opts, "daemon")->enabled)
611
int bigsleep, checks;
615
sigaction (SIGTERM, &sigact, NULL);
616
sigaction (SIGHUP, &sigact, NULL);
617
sigaction (SIGCHLD, &sigact, NULL);
620
checks = optget (opts, "Checks")->numarg;
624
logg ("^Number of checks must be a positive integer.\n");
629
if (!optget (opts, "DNSDatabaseInfo")->enabled
630
|| optget (opts, "no-dns")->enabled)
634
logg ("^Number of checks must be between 1 and 50.\n");
640
bigsleep = 24 * 3600 / checks;
643
if (!optget (opts, "Foreground")->enabled)
645
if (daemonize () == -1)
647
logg ("!daemonize() failed\n");
649
return FCE_FAILEDUPDATE;
652
mprintf_disabled = 1;
656
if ((opt = optget (opts, "PidFile"))->enabled)
658
pidfile = opt->strarg;
664
logg ("#freshclam daemon %s (OS: " TARGET_OS_TYPE ", ARCH: "
665
TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ")\n",
670
ret = download (opts, cfgfile);
674
if ((opt = optget (opts, "OnErrorExecute"))->enabled)
678
execute ("OnErrorExecute", arg, opts);
683
logg ("#--------------------------------------\n");
685
sigaction (SIGALRM, &sigact, &oldact);
688
sigaction (SIGUSR1, &sigact, &oldact);
702
while (!terminate && now < wakeup);
706
logg ("Received signal: wake up\n");
709
else if (terminate == -2)
711
logg ("Received signal: re-opening log file\n");
718
sigaction (SIGALRM, &oldact, NULL);
721
sigaction (SIGUSR1, &oldact, NULL);
728
ret = download (opts, cfgfile);
733
if ((opt = optget (opts, "OnErrorExecute"))->enabled)
734
execute ("OnErrorExecute", opt->strarg, opts);
749
void submit_host_info(struct optstruct *opts)
751
struct optstruct *opt;
752
struct cl_engine *engine;
755
if (!optget(opts, "enable-stats")->enabled)
758
engine = cl_engine_new();
762
if (optget (opts, "Debug")->enabled || optget (opts, "debug")->enabled)
765
if (optget (opts, "verbose")->enabled)
768
cl_engine_stats_enable(engine);
770
intel = engine->stats_data;
772
engine->cb_stats_submit = NULL;
773
cl_engine_free(engine);
777
intel->host_info = calloc(1, strlen(TARGET_OS_TYPE) + strlen(TARGET_ARCH_TYPE) + 2);
778
if (!(intel->host_info)) {
779
engine->cb_stats_submit = NULL;
780
cl_engine_free(engine);
784
sprintf(intel->host_info, "%s %s", TARGET_OS_TYPE, TARGET_ARCH_TYPE);
786
if (!strcmp(hostid, "none"))
787
cl_engine_set_clcb_stats_get_hostid(engine, NULL);
788
else if (strcmp(hostid, "default"))
789
cl_engine_set_clcb_stats_get_hostid(engine, get_hostid);
791
if (optget(opts, "stats-timeout")->enabled) {
792
cl_engine_set_num(engine, CL_ENGINE_STATS_TIMEOUT, optget(opts, "StatsTimeout")->numarg);
795
cl_engine_free(engine);
798
int is_valid_hostid(void)
802
if (strlen(hostid) != 36)
806
for (i=0; i < 36; i++)
807
if (hostid[i] == '-')
813
if (hostid[8] != '-' || hostid[13] != '-' || hostid[18] != '-' || hostid[23] != '-')
819
char *get_hostid(void *cbdata)
821
if (!strcmp(hostid, "none"))
824
if (!is_valid_hostid())
825
return strdup(STATS_ANON_UUID);
827
logg("HostID is valid: %s\n", hostid);
829
return strdup(hostid);