/*
* Copyright ( C ) 2002 - 2006 Tomasz Kojm < tkojm @ clamav . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
# ifdef _MSC_VER
# include <winsock.h>
# endif
# if HAVE_CONFIG_H
# include "clamav-config.h"
# endif
# include <stdio.h>
# include <stdlib.h>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# include <string.h>
# include <errno.h>
# include <signal.h>
# include <time.h>
# include <sys/types.h>
# ifndef C_WINDOWS
# include <sys/wait.h>
# endif
# include <sys/stat.h>
# include <fcntl.h>
# ifndef C_WINDOWS
# include <pwd.h>
# include <grp.h>
# endif
# if defined(USE_SYSLOG) && !defined(C_AIX)
# include <syslog.h>
# endif
# include "target.h"
# include "clamav.h"
# include "shared/options.h"
# include "shared/output.h"
# include "shared/misc.h"
# include "execute.h"
# include "manager.h"
# include "mirman.h"
static short terminate = 0 ;
extern int active_children ;
static short foreground = 1 ;
static void daemon_sighandler ( int sig ) {
switch ( sig ) {
# ifdef SIGCHLD
case SIGCHLD :
waitpid ( - 1 , NULL , WNOHANG ) ;
active_children - - ;
break ;
# endif
# ifdef SIGALRM
case SIGALRM :
terminate = - 1 ;
break ;
# endif
# ifdef SIGUSR1
case SIGUSR1 :
terminate = - 1 ;
break ;
# endif
# ifdef SIGHUP
case SIGHUP :
terminate = - 2 ;
break ;
# endif
default :
terminate = 1 ;
break ;
}
return ;
}
static void writepid ( char * pidfile )
{
FILE * fd ;
int old_umask ;
old_umask = umask ( 0006 ) ;
if ( ( fd = fopen ( pidfile , " w " ) ) = = NULL ) {
logg ( " !Can't save PID to file %s: %s \n " , pidfile , strerror ( errno ) ) ;
} else {
fprintf ( fd , " %d " , ( int ) getpid ( ) ) ;
fclose ( fd ) ;
}
umask ( old_umask ) ;
}
static void help ( void )
{
mprintf_stdout = 1 ;
mprintf ( " \n " ) ;
mprintf ( " Clam AntiVirus: freshclam " VERSION " \n " ) ;
mprintf ( " (C) 2002 - 2007 ClamAV Team - http://www.clamav.net/team \n \n " ) ;
mprintf ( " --help -h show help \n " ) ;
mprintf ( " --version -V print version number and exit \n " ) ;
mprintf ( " --verbose -v be verbose \n " ) ;
mprintf ( " --debug enable debug messages \n " ) ;
mprintf ( " --quiet only output error messages \n " ) ;
mprintf ( " --no-warnings don't print and log warnings \n " ) ;
mprintf ( " --stdout write to stdout instead of stderr \n " ) ;
mprintf ( " \n " ) ;
mprintf ( " --config-file=FILE read configuration from FILE. \n " ) ;
mprintf ( " --log=FILE -l FILE log into FILE \n " ) ;
mprintf ( " --daemon -d run in daemon mode \n " ) ;
mprintf ( " --pid=FILE -p FILE save daemon's pid in FILE \n " ) ;
mprintf ( " --user=USER -u USER run as USER \n " ) ;
mprintf ( " --no-dns force old non-DNS verification method \n " ) ;
mprintf ( " --checks=#n -c #n number of checks per day, 1 <= n <= 50 \n " ) ;
mprintf ( " --datadir=DIRECTORY download new databases into DIRECTORY \n " ) ;
# ifdef BUILD_CLAMD
mprintf ( " --daemon-notify[=/path/clamd.conf] send RELOAD command to clamd \n " ) ;
# endif
mprintf ( " --local-address=IP -a IP bind to IP for HTTP downloads \n " ) ;
mprintf ( " --on-update-execute=COMMAND execute COMMAND after successful update \n " ) ;
mprintf ( " --on-error-execute=COMMAND execute COMMAND if errors occured \n " ) ;
mprintf ( " --on-outdated-execute=COMMAND execute COMMAND when software is outdated \n " ) ;
mprintf ( " --list-mirrors print mirrors from mirrors.dat \n " ) ;
mprintf ( " \n " ) ;
}
static int download ( const struct cfgstruct * copt , const struct optstruct * opt , const char * datadir )
{
int ret = 0 , try = 0 , maxattempts = 0 ;
const struct cfgstruct * cpt ;
maxattempts = cfgopt ( copt , " MaxAttempts " ) - > numarg ;
logg ( " *Max retries == %d \n " , maxattempts ) ;
if ( ! ( cpt = cfgopt ( copt , " DatabaseMirror " ) ) - > enabled ) {
logg ( " ^You must specify at least one database mirror. \n " ) ;
return 56 ;
} else {
while ( cpt ) {
ret = downloadmanager ( copt , opt , cpt - > strarg , datadir , try = = maxattempts - 1 ) ;
alarm ( 0 ) ;
if ( ret = = 52 | | ret = = 54 | | ret = = 58 | | ret = = 59 ) {
if ( try < maxattempts - 1 ) {
logg ( " Trying again in 5 secs... \n " ) ;
try + + ;
sleep ( 5 ) ;
continue ;
} else {
logg ( " Giving up on %s... \n " , cpt - > strarg ) ;
cpt = ( struct cfgstruct * ) cpt - > nextarg ;
if ( ! cpt ) {
logg ( " Update failed. Your network may be down or none of the mirrors listed in freshclam.conf is working. Check http://www.clamav.net/support/mirror-problem for possible reasons. \n " ) ;
}
try = 0 ;
}
} else {
return ret ;
}
}
}
return ret ;
}
int main ( int argc , char * * argv )
{
int ret = 52 ;
const char * newdir , * cfgfile ;
char * pidfile = NULL ;
struct cfgstruct * copt ;
const struct cfgstruct * cpt ;
# ifndef C_WINDOWS
struct sigaction sigact ;
struct sigaction oldact ;
# endif
# if !defined(C_CYGWIN) && !defined(C_OS2) && !defined(C_WINDOWS)
char * unpuser ;
struct passwd * user ;
# endif
struct stat statbuf ;
struct mirdat mdat ;
struct optstruct * opt ;
const char * short_options = " hvdp:Vl:c:u:a: " ;
static struct option long_options [ ] = {
{ " help " , 0 , 0 , ' h ' } ,
{ " quiet " , 0 , 0 , 0 } ,
{ " no-warnings " , 0 , 0 , 0 } ,
{ " verbose " , 0 , 0 , ' v ' } ,
{ " debug " , 0 , 0 , 0 } ,
{ " version " , 0 , 0 , ' V ' } ,
{ " datadir " , 1 , 0 , 0 } ,
{ " log " , 1 , 0 , ' l ' } ,
{ " log-verbose " , 0 , 0 , 0 } , /* not used */
{ " stdout " , 0 , 0 , 0 } ,
{ " daemon " , 0 , 0 , ' d ' } ,
{ " pid " , 1 , 0 , ' p ' } ,
{ " user " , 1 , 0 , ' u ' } , /* not used */
{ " config-file " , 1 , 0 , 0 } ,
{ " no-dns " , 0 , 0 , 0 } ,
{ " checks " , 1 , 0 , ' c ' } ,
{ " http-proxy " , 1 , 0 , 0 } ,
{ " local-address " , 1 , 0 , ' a ' } ,
{ " proxy-user " , 1 , 0 , 0 } ,
{ " daemon-notify " , 2 , 0 , 0 } ,
{ " on-update-execute " , 1 , 0 , 0 } ,
{ " on-error-execute " , 1 , 0 , 0 } ,
{ " on-outdated-execute " , 1 , 0 , 0 } ,
{ " list-mirrors " , 0 , 0 , 0 } ,
{ 0 , 0 , 0 , 0 }
} ;
opt = opt_parse ( argc , argv , short_options , long_options , NULL ) ;
if ( ! opt ) {
mprintf ( " !Can't parse the command line \n " ) ;
return 40 ;
}
if ( opt_check ( opt , " help " ) ) {
help ( ) ;
opt_free ( opt ) ;
return 0 ;
}
/* parse the config file */
if ( ( cfgfile = opt_arg ( opt , " config-file " ) ) ) {
copt = getcfg ( cfgfile , 1 ) ;
} else {
/* TODO: force strict permissions on freshclam.conf */
if ( ( copt = getcfg ( ( cfgfile = CONFDIR " /freshclam.conf " ) , 1 ) ) = = NULL )
copt = getcfg ( ( cfgfile = CONFDIR " /clamd.conf " ) , 1 ) ;
}
if ( ! copt ) {
logg ( " !Can't parse the config file %s \n " , cfgfile ) ;
opt_free ( opt ) ;
return 56 ;
}
if ( opt_check ( opt , " datadir " ) )
newdir = opt_arg ( opt , " datadir " ) ;
else
newdir = cfgopt ( copt , " DatabaseDirectory " ) - > strarg ;
if ( opt_check ( opt , " version " ) ) {
print_version ( newdir ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 0 ;
}
# ifdef C_WINDOWS
if ( ! pthread_win32_process_attach_np ( ) ) {
mprintf ( " !Can't start the win32 pthreads layer \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 63 ;
}
# endif
if ( opt_check ( opt , " http-proxy " ) | | opt_check ( opt , " proxy-user " ) )
logg ( " WARNING: Proxy settings are now only configurable in the config file. \n " ) ;
if ( cfgopt ( copt , " HTTPProxyPassword " ) - > enabled ) {
if ( stat ( cfgfile , & statbuf ) = = - 1 ) {
logg ( " ^Can't stat %s (critical error) \n " , cfgfile ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 56 ;
}
# if !defined(C_CYGWIN) && !defined(C_WINDOWS)
if ( statbuf . st_mode & ( S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH ) ) {
logg ( " ^Insecure permissions (for HTTPProxyPassword): %s must have no more than 0700 permissions. \n " , cfgfile ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 56 ;
}
# endif
}
# if !defined(C_CYGWIN) && !defined(C_OS2) && !defined(C_WINDOWS)
/* freshclam shouldn't work with root privileges */
if ( opt_check ( opt , " user " ) )
unpuser = opt_arg ( opt , " user " ) ;
else
unpuser = cfgopt ( copt , " DatabaseOwner " ) - > strarg ;
if ( ! geteuid ( ) ) {
if ( ( user = getpwnam ( unpuser ) ) = = NULL ) {
logg ( " ^Can't get information about user %s. \n " , unpuser ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 60 ;
}
if ( cfgopt ( copt , " AllowSupplementaryGroups " ) - > enabled ) {
# ifdef HAVE_INITGROUPS
if ( initgroups ( unpuser , user - > pw_gid ) ) {
logg ( " ^initgroups() failed. \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 61 ;
}
# endif
} else {
# ifdef HAVE_SETGROUPS
if ( setgroups ( 1 , & user - > pw_gid ) ) {
logg ( " ^setgroups() failed. \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 61 ;
}
# endif
}
if ( setgid ( user - > pw_gid ) ) {
logg ( " ^setgid(%d) failed. \n " , ( int ) user - > pw_gid ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 61 ;
}
if ( setuid ( user - > pw_uid ) ) {
logg ( " ^setuid(%d) failed. \n " , ( int ) user - > pw_uid ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 61 ;
}
}
# endif
/* initialize some important variables */
if ( opt_check ( opt , " debug " ) | | cfgopt ( copt , " Debug " ) - > enabled )
cl_debug ( ) ;
if ( opt_check ( opt , " verbose " ) )
mprintf_verbose = 1 ;
if ( opt_check ( opt , " quiet " ) )
mprintf_quiet = 1 ;
if ( opt_check ( opt , " no-warnings " ) ) {
mprintf_nowarn = 1 ;
logg_nowarn = 1 ;
}
if ( opt_check ( opt , " stdout " ) )
mprintf_stdout = 1 ;
/* initialize logger */
logg_verbose = cfgopt ( copt , " LogVerbose " ) - > enabled ;
logg_time = cfgopt ( copt , " LogTime " ) - > enabled ;
logg_size = cfgopt ( copt , " LogFileMaxSize " ) - > numarg ;
if ( opt_check ( opt , " log " ) ) {
logg_file = opt_arg ( opt , " log " ) ;
if ( logg ( " #-------------------------------------- \n " ) ) {
mprintf ( " !Problem with internal logger (--log=%s). \n " , logg_file ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 62 ;
}
} else if ( ( cpt = cfgopt ( copt , " UpdateLogFile " ) ) - > enabled ) {
logg_file = cpt - > strarg ;
if ( logg ( " #-------------------------------------- \n " ) ) {
mprintf ( " !Problem with internal logger (UpdateLogFile = %s). \n " , logg_file ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 62 ;
}
} else
logg_file = NULL ;
# if defined(USE_SYSLOG) && !defined(C_AIX)
if ( cfgopt ( copt , " LogSyslog " ) - > enabled ) {
int fac = LOG_LOCAL6 ;
if ( ( cpt = cfgopt ( copt , " LogFacility " ) ) - > enabled ) {
if ( ( fac = logg_facility ( cpt - > strarg ) ) = = - 1 ) {
mprintf ( " !LogFacility: %s: No such facility. \n " , cpt - > strarg ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 62 ;
}
}
openlog ( " freshclam " , LOG_PID , fac ) ;
logg_syslog = 1 ;
}
# endif
/* change the current working directory */
if ( chdir ( newdir ) ) {
logg ( " Can't change dir to %s \n " , newdir ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 50 ;
} else
logg ( " *Current working dir is %s \n " , newdir ) ;
if ( opt_check ( opt , " list-mirrors " ) ) {
if ( mirman_read ( " mirrors.dat " , & mdat , 1 ) = = - 1 ) {
printf ( " Can't read mirrors.dat \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 55 ;
}
mirman_list ( & mdat ) ;
mirman_free ( & mdat ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 0 ;
}
# ifdef C_WINDOWS
{
WSADATA wsaData ;
if ( WSAStartup ( MAKEWORD ( 2 , 2 ) , & wsaData ) ! = NO_ERROR ) {
logg ( " !Error at WSAStartup(): %d \n " , WSAGetLastError ( ) ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 1 ;
}
}
# endif
if ( opt_check ( opt , " daemon " ) ) {
int bigsleep , checks ;
# ifndef C_WINDOWS
time_t now , wakeup ;
memset ( & sigact , 0 , sizeof ( struct sigaction ) ) ;
sigact . sa_handler = daemon_sighandler ;
# endif
if ( opt_check ( opt , " checks " ) )
checks = atoi ( opt_arg ( opt , " checks " ) ) ;
else
checks = cfgopt ( copt , " Checks " ) - > numarg ;
if ( checks < = 0 ) {
logg ( " ^Number of checks must be a positive integer. \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 41 ;
}
if ( ! cfgopt ( copt , " DNSDatabaseInfo " ) - > enabled | | opt_check ( opt , " no-dns " ) ) {
if ( checks > 50 ) {
logg ( " ^Number of checks must be between 1 and 50. \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 41 ;
}
}
bigsleep = 24 * 3600 / checks ;
if ( ! cfgopt ( copt , " Foreground " ) - > enabled ) {
if ( daemonize ( ) = = - 1 ) {
logg ( " !daemonize() failed \n " ) ;
opt_free ( opt ) ;
freecfg ( copt ) ;
return 70 ; /* FIXME */
}
foreground = 0 ;
mprintf_disabled = 1 ;
}
if ( opt_check ( opt , " pid " ) ) {
pidfile = opt_arg ( opt , " pid " ) ;
} else if ( ( cpt = cfgopt ( copt , " PidFile " ) ) - > enabled ) {
pidfile = cpt - > strarg ;
}
if ( pidfile ) {
writepid ( pidfile ) ;
}
active_children = 0 ;
logg ( " #freshclam daemon " VERSION " (OS: " TARGET_OS_TYPE " , ARCH: " TARGET_ARCH_TYPE " , CPU: " TARGET_CPU_TYPE " ) \n " ) ;
# ifdef C_WINDOWS
signal ( SIGINT , daemon_sighandler ) ;
terminate = 0 ;
# else
sigaction ( SIGTERM , & sigact , NULL ) ;
sigaction ( SIGHUP , & sigact , NULL ) ;
sigaction ( SIGINT , & sigact , NULL ) ;
sigaction ( SIGCHLD , & sigact , NULL ) ;
# endif
while ( ! terminate ) {
ret = download ( copt , opt , newdir ) ;
if ( ret > 1 ) {
const char * arg = NULL ;
if ( opt_check ( opt , " on-error-execute " ) )
arg = opt_arg ( opt , " on-error-execute " ) ;
else if ( ( cpt = cfgopt ( copt , " OnErrorExecute " ) ) - > enabled )
arg = cpt - > strarg ;
if ( arg )
execute ( " OnErrorExecute " , arg ) ;
}
logg ( " #-------------------------------------- \n " ) ;
# ifdef SIGALRM
sigaction ( SIGALRM , & sigact , & oldact ) ;
# endif
# ifdef SIGUSR1
sigaction ( SIGUSR1 , & sigact , & oldact ) ;
# endif
# ifdef C_WINDOWS
sleep ( bigsleep ) ;
# else
time ( & wakeup ) ;
wakeup + = bigsleep ;
alarm ( bigsleep ) ;
do {
pause ( ) ;
time ( & now ) ;
} while ( ! terminate & & now < wakeup ) ;
if ( terminate = = - 1 ) {
logg ( " Received signal: wake up \n " ) ;
terminate = 0 ;
} else if ( terminate = = - 2 ) {
logg ( " Received signal: re-opening log file \n " ) ;
terminate = 0 ;
logg_close ( ) ;
}
# endif
# ifdef SIGALRM
sigaction ( SIGALRM , & oldact , NULL ) ;
# endif
# ifdef SIGUSR1
sigaction ( SIGUSR1 , & oldact , NULL ) ;
# endif
}
} else
ret = download ( copt , opt , newdir ) ;
if ( opt_check ( opt , " on-error-execute " ) ) {
if ( ret > 1 )
if ( system ( opt_arg ( opt , " on-error-execute " ) ) = = - 1 )
logg ( " !system(%s) failed \n " , opt_arg ( opt , " on-error-execute " ) ) ;
} else if ( ( cpt = cfgopt ( copt , " OnErrorExecute " ) ) - > enabled ) {
if ( ret > 1 )
if ( system ( cpt - > strarg ) = = - 1 )
logg ( " !system(%s) failed \n " , cpt - > strarg ) ;
}
if ( pidfile ) {
unlink ( pidfile ) ;
}
opt_free ( opt ) ;
freecfg ( copt ) ;
# ifdef C_WINDOWS
WSACleanup ( ) ;
if ( ! pthread_win32_process_detach_np ( ) ) {
mprintf ( " !Can't stop the win32 pthreads layer \n " ) ;
return 63 ;
}
# endif
return ( ret ) ;
}