source: clamav/trunk/freshclam/freshclam.c@ 191

Last change on this file since 191 was 191, checked in by Yuri Dario, 14 years ago

clamav: applied os2 patches.

File size: 13.1 KB
Line 
1/*
2 * Copyright (C) 2002 - 2006 Tomasz Kojm <tkojm@clamav.net>
3 *
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.
7 *
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.
12 *
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,
16 * MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20#include "clamav-config.h"
21#endif
22
23#include <stdio.h>
24#include <stdlib.h>
25#ifdef HAVE_UNISTD_H
26#include <unistd.h>
27#endif
28#include <string.h>
29#include <errno.h>
30#include <signal.h>
31#include <time.h>
32#include <sys/types.h>
33#ifndef _WIN32
34#include <sys/wait.h>
35#endif
36#include <sys/stat.h>
37#include <fcntl.h>
38#ifdef HAVE_PWD_H
39#include <pwd.h>
40#endif
41#ifdef HAVE_GRP_H
42#include <grp.h>
43#endif
44
45#if defined(USE_SYSLOG) && !defined(C_AIX)
46#include <syslog.h>
47#endif
48
49#include "target.h"
50#include "clamav.h"
51
52#include "libclamav/others.h"
53
54#include "shared/optparser.h"
55#include "shared/output.h"
56#include "shared/misc.h"
57
58#include "execute.h"
59#include "manager.h"
60#include "mirman.h"
61
62static short terminate = 0;
63extern int active_children;
64
65static short foreground = 1;
66char updtmpdir[512];
67
68static void sighandler(int sig) {
69
70 switch(sig) {
71#ifdef SIGCHLD
72 case SIGCHLD:
73 waitpid(-1, NULL, WNOHANG);
74 active_children--;
75 break;
76#endif
77
78#ifdef SIGALRM
79 case SIGALRM:
80 terminate = -1;
81 break;
82#endif
83#ifdef SIGUSR1
84 case SIGUSR1:
85 terminate = -1;
86 break;
87#endif
88
89#ifdef SIGHUP
90 case SIGHUP:
91 terminate = -2;
92 break;
93#endif
94
95 default:
96 if(*updtmpdir)
97 cli_rmdirs(updtmpdir);
98 logg("Update process interrupted\n");
99 exit(2);
100 }
101
102 return;
103}
104
105static void writepid(const char *pidfile)
106{
107 FILE *fd;
108 int old_umask;
109 old_umask = umask(0006);
110 if((fd = fopen(pidfile, "w")) == NULL) {
111 logg("!Can't save PID to file %s: %s\n", pidfile, strerror(errno));
112 } else {
113 fprintf(fd, "%d", (int) getpid());
114 fclose(fd);
115 }
116 umask(old_umask);
117}
118
119static void help(void)
120{
121 mprintf_stdout = 1;
122
123 mprintf("\n");
124 mprintf(" Clam AntiVirus: freshclam %s\n", get_version());
125 printf(" By The ClamAV Team: http://www.clamav.net/team\n");
126 printf(" (C) 2007-2009 Sourcefire, Inc. et al.\n\n");
127
128 mprintf(" --help -h show help\n");
129 mprintf(" --version -V print version number and exit\n");
130 mprintf(" --verbose -v be verbose\n");
131 mprintf(" --debug enable debug messages\n");
132 mprintf(" --quiet only output error messages\n");
133 mprintf(" --no-warnings don't print and log warnings\n");
134 mprintf(" --stdout write to stdout instead of stderr\n");
135 mprintf("\n");
136 mprintf(" --config-file=FILE read configuration from FILE.\n");
137 mprintf(" --log=FILE -l FILE log into FILE\n");
138#ifndef _WIN32
139 mprintf(" --daemon -d run in daemon mode\n");
140 mprintf(" --pid=FILE -p FILE save daemon's pid in FILE\n");
141 mprintf(" --user=USER -u USER run as USER\n");
142#endif
143 mprintf(" --no-dns force old non-DNS verification method\n");
144 mprintf(" --checks=#n -c #n number of checks per day, 1 <= n <= 50\n");
145 mprintf(" --datadir=DIRECTORY download new databases into DIRECTORY\n");
146#ifdef BUILD_CLAMD
147 mprintf(" --daemon-notify[=/path/clamd.conf] send RELOAD command to clamd\n");
148#endif
149 mprintf(" --local-address=IP -a IP bind to IP for HTTP downloads\n");
150 mprintf(" --on-update-execute=COMMAND execute COMMAND after successful update\n");
151 mprintf(" --on-error-execute=COMMAND execute COMMAND if errors occured\n");
152 mprintf(" --on-outdated-execute=COMMAND execute COMMAND when software is outdated\n");
153 mprintf(" --list-mirrors print mirrors from mirrors.dat\n");
154 mprintf(" --submit-stats[=/path/clamd.conf] only submit detection statistics\n");
155
156 mprintf("\n");
157}
158
159static int download(const struct optstruct *opts, const char *datadir, const char *cfgfile)
160{
161 int ret = 0, try = 0, maxattempts = 0;
162 const struct optstruct *opt;
163
164
165 maxattempts = optget(opts, "MaxAttempts")->numarg;
166 logg("*Max retries == %d\n", maxattempts);
167
168 if(!(opt = optget(opts, "DatabaseMirror"))->enabled) {
169 logg("^You must specify at least one database mirror in %s\n", cfgfile);
170 return 56;
171 } else {
172 while(opt) {
173 ret = downloadmanager(opts, opt->strarg, datadir, try == maxattempts - 1);
174#ifndef _WIN32
175 alarm(0);
176#endif
177 if(ret == 52 || ret == 54 || ret == 58 || ret == 59) {
178 if(try < maxattempts - 1) {
179 logg("Trying again in 5 secs...\n");
180 try++;
181 sleep(5);
182 continue;
183 } else {
184 logg("Giving up on %s...\n", opt->strarg);
185 opt = (struct optstruct *) opt->nextarg;
186 if(!opt) {
187 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);
188 }
189 try = 0;
190 }
191
192 } else {
193 return ret;
194 }
195 }
196 }
197
198 return ret;
199}
200
201int main(int argc, char **argv)
202{
203 int ret = 52, retcl;
204 const char *cfgfile, *arg = NULL, *pidfile = NULL;
205 char *pt, dbdir[512];
206 struct optstruct *opts;
207 const struct optstruct *opt;
208#ifndef _WIN32
209 struct sigaction sigact;
210 struct sigaction oldact;
211#endif
212#ifdef HAVE_PWD_H
213 const char *dbowner;
214 struct passwd *user;
215#endif
216 struct stat statbuf;
217 struct mirdat mdat;
218
219 if(check_flevel())
220 exit(40);
221
222 if((retcl = cl_init(CL_INIT_DEFAULT))) {
223 mprintf("!Can't initialize libclamav: %s\n", cl_strerror(retcl));
224 return 40;
225 }
226
227 if((opts = optparse(NULL, argc, argv, 1, OPT_FRESHCLAM, 0, NULL)) == NULL) {
228 mprintf("!Can't parse command line options\n");
229 return 40;
230 }
231
232 if(optget(opts, "help")->enabled) {
233 help();
234 optfree(opts);
235 return 0;
236 }
237
238 /* parse the config file */
239 cfgfile = optget(opts, "config-file")->strarg;
240 pt = strdup(cfgfile);
241 if((opts = optparse(cfgfile, 0, NULL, 1, OPT_FRESHCLAM, 0, opts)) == NULL) {
242 fprintf(stderr, "ERROR: Can't open/parse the config file %s\n", pt);
243 free(pt);
244 return 40;
245 }
246 free(pt);
247
248 if(optget(opts, "version")->enabled) {
249 print_version(optget(opts, "DatabaseDirectory")->strarg);
250 optfree(opts);
251 return 0;
252 }
253
254 if(optget(opts, "HTTPProxyPassword")->enabled) {
255 if(stat(cfgfile, &statbuf) == -1) {
256 logg("^Can't stat %s (critical error)\n", cfgfile);
257 optfree(opts);
258 return 56;
259 }
260
261#if !defined(_WIN32) && !defined(C_OS2)
262 if(statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)) {
263 logg("^Insecure permissions (for HTTPProxyPassword): %s must have no more than 0700 permissions.\n", cfgfile);
264 optfree(opts);
265 return 56;
266 }
267#endif
268 }
269
270#ifdef HAVE_PWD_H
271#if !defined(C_OS2)
272 /* freshclam shouldn't work with root privileges */
273 dbowner = optget(opts, "DatabaseOwner")->strarg;
274
275 if(!geteuid()) {
276 if((user = getpwnam(dbowner)) == NULL) {
277 logg("^Can't get information about user %s.\n", dbowner);
278 optfree(opts);
279 return 60;
280 }
281
282 if(optget(opts, "AllowSupplementaryGroups")->enabled) {
283#ifdef HAVE_INITGROUPS
284 if(initgroups(dbowner, user->pw_gid)) {
285 logg("^initgroups() failed.\n");
286 optfree(opts);
287 return 61;
288 }
289#endif
290 } else {
291#ifdef HAVE_SETGROUPS
292 if(setgroups(1, &user->pw_gid)) {
293 logg("^setgroups() failed.\n");
294 optfree(opts);
295 return 61;
296 }
297#endif
298 }
299
300 if(setgid(user->pw_gid)) {
301 logg("^setgid(%d) failed.\n", (int) user->pw_gid);
302 optfree(opts);
303 return 61;
304 }
305
306 if(setuid(user->pw_uid)) {
307 logg("^setuid(%d) failed.\n", (int) user->pw_uid);
308 optfree(opts);
309 return 61;
310 }
311 }
312#endif /* C_OS2 */
313#endif /* HAVE_PWD_H */
314
315 /* initialize some important variables */
316
317 if(optget(opts, "Debug")->enabled || optget(opts, "debug")->enabled)
318 cl_debug();
319
320 if(optget(opts, "verbose")->enabled)
321 mprintf_verbose = 1;
322
323 if(optget(opts, "quiet")->enabled)
324 mprintf_quiet = 1;
325
326 if(optget(opts, "no-warnings")->enabled) {
327 mprintf_nowarn = 1;
328 logg_nowarn = 1;
329 }
330
331 if(optget(opts, "stdout")->enabled)
332 mprintf_stdout = 1;
333
334 /* initialize logger */
335 logg_verbose = mprintf_verbose ? 1 : optget(opts, "LogVerbose")->enabled;
336 logg_time = optget(opts, "LogTime")->enabled;
337 logg_size = optget(opts, "LogFileMaxSize")->numarg;
338
339 if((opt = optget(opts, "UpdateLogFile"))->enabled) {
340 logg_file = opt->strarg;
341 if(logg("#--------------------------------------\n")) {
342 mprintf("!Problem with internal logger (UpdateLogFile = %s).\n", logg_file);
343 optfree(opts);
344 return 62;
345 }
346 } else
347 logg_file = NULL;
348
349#if defined(USE_SYSLOG) && !defined(C_AIX)
350 if(optget(opts, "LogSyslog")->enabled) {
351 int fac = LOG_LOCAL6;
352
353 if((opt = optget(opts, "LogFacility"))->enabled) {
354 if((fac = logg_facility(opt->strarg)) == -1) {
355 mprintf("!LogFacility: %s: No such facility.\n", opt->strarg);
356 optfree(opts);
357 return 62;
358 }
359 }
360
361 openlog("freshclam", LOG_PID, fac);
362 logg_syslog = 1;
363 }
364#endif
365
366 /* change the current working directory */
367 if(chdir(optget(opts, "DatabaseDirectory")->strarg)) {
368 logg("!Can't change dir to %s\n", optget(opts, "DatabaseDirectory")->strarg);
369 optfree(opts);
370 return 50;
371 } else {
372 if(!getcwd(dbdir, sizeof(dbdir))) {
373 logg("!getcwd() failed\n");
374 optfree(opts);
375 return 50;
376 }
377 logg("*Current working dir is %s\n", dbdir);
378 }
379
380
381 if(optget(opts, "list-mirrors")->enabled) {
382 if(mirman_read("mirrors.dat", &mdat, 1) == -1) {
383 printf("Can't read mirrors.dat\n");
384 optfree(opts);
385 return 55;
386 }
387 mirman_list(&mdat);
388 mirman_free(&mdat);
389 optfree(opts);
390 return 0;
391 }
392
393 *updtmpdir = 0;
394
395#ifdef _WIN32
396 signal(SIGINT, sighandler);
397#else
398 memset(&sigact, 0, sizeof(struct sigaction));
399 sigact.sa_handler = sighandler;
400 sigaction(SIGINT, &sigact, NULL);
401#endif
402 if(optget(opts, "daemon")->enabled) {
403 int bigsleep, checks;
404#ifndef _WIN32
405 time_t now, wakeup;
406
407 sigaction(SIGTERM, &sigact, NULL);
408 sigaction(SIGHUP, &sigact, NULL);
409 sigaction(SIGCHLD, &sigact, NULL);
410#endif
411
412 checks = optget(opts, "Checks")->numarg;
413
414 if(checks <= 0) {
415 logg("^Number of checks must be a positive integer.\n");
416 optfree(opts);
417 return 41;
418 }
419
420 if(!optget(opts, "DNSDatabaseInfo")->enabled || optget(opts, "no-dns")->enabled) {
421 if(checks > 50) {
422 logg("^Number of checks must be between 1 and 50.\n");
423 optfree(opts);
424 return 41;
425 }
426 }
427
428 bigsleep = 24 * 3600 / checks;
429
430#ifndef _WIN32
431 if(!optget(opts, "Foreground")->enabled) {
432 if(daemonize() == -1) {
433 logg("!daemonize() failed\n");
434 optfree(opts);
435 return 70; /* FIXME */
436 }
437 foreground = 0;
438 mprintf_disabled = 1;
439 }
440#endif
441
442 if((opt = optget(opts, "PidFile"))->enabled) {
443 pidfile = opt->strarg;
444 writepid(pidfile);
445 }
446
447 active_children = 0;
448
449 logg("#freshclam daemon %s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")\n", get_version());
450
451 while(!terminate) {
452 ret = download(opts, dbdir, cfgfile);
453
454 if(ret <= 1) {
455 if((opt = optget(opts, "SubmitDetectionStats"))->enabled)
456 submitstats(opt->strarg, opts);
457 } else {
458 if((opt = optget(opts, "OnErrorExecute"))->enabled)
459 arg = opt->strarg;
460
461 if(arg)
462 execute("OnErrorExecute", arg, opts);
463
464 arg = NULL;
465 }
466
467 logg("#--------------------------------------\n");
468#ifdef SIGALRM
469 sigaction(SIGALRM, &sigact, &oldact);
470#endif
471#ifdef SIGUSR1
472 sigaction(SIGUSR1, &sigact, &oldact);
473#endif
474
475#ifdef _WIN32
476 sleep(bigsleep);
477#else
478 time(&wakeup);
479 wakeup += bigsleep;
480 alarm(bigsleep);
481 do {
482 pause();
483 time(&now);
484 } while (!terminate && now < wakeup);
485
486 if (terminate == -1) {
487 logg("Received signal: wake up\n");
488 terminate = 0;
489 } else if (terminate == -2) {
490 logg("Received signal: re-opening log file\n");
491 terminate = 0;
492 logg_close();
493 }
494#endif
495
496#ifdef SIGALRM
497 sigaction(SIGALRM, &oldact, NULL);
498#endif
499#ifdef SIGUSR1
500 sigaction(SIGUSR1, &oldact, NULL);
501#endif
502 }
503
504 } else {
505 if((opt = optget(opts, "submit-stats"))->active) {
506 if(!optget(opts, "no-warnings")->enabled)
507 logg(" *** Virus databases are not updated in this mode ***\n");
508 ret = submitstats(opt->strarg, opts);
509 } else {
510 ret = download(opts, dbdir, cfgfile);
511
512 if((opt = optget(opts, "SubmitDetectionStats"))->enabled)
513 submitstats(opt->strarg, opts);
514 }
515 }
516
517 if(ret > 1) {
518 if((opt = optget(opts, "OnErrorExecute"))->enabled)
519 execute("OnErrorExecute", opt->strarg, opts);
520 }
521
522 if (pidfile) {
523 unlink(pidfile);
524 }
525
526 optfree(opts);
527
528 return(ret);
529}
Note: See TracBrowser for help on using the repository browser.