shared/output.c
afb48b28
 /*
086eab5c
  *  Copyright (C) 2007-2009 Sourcefire, Inc.
  *
  *  Authors: Tomasz Kojm
afb48b28
  *
  *  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.
afb48b28
  *
  *  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.
afb48b28
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
ace24e1f
 #ifdef HAVE_UNISTD_H
afb48b28
 #include <unistd.h>
ace24e1f
 #endif
afb48b28
 #include <fcntl.h>
 #include <time.h>
 #include <sys/stat.h>
 #include <errno.h>
4cd80898
 #ifndef _WIN32
afb48b28
 #include <sys/time.h>
 #include <sys/socket.h>
ace24e1f
 #endif
afb48b28
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
 
0378a9ab
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
 
afb48b28
 #if defined(USE_SYSLOG) && !defined(C_AIX)
 #include <syslog.h>
 #endif
 
 #include "output.h"
9e751804
 #include "libclamav/others.h"
afb48b28
 
2ed08e8a
 #ifdef CL_NOTHREADS
 #undef CL_THREAD_SAFE
 #endif
 
afb48b28
 #ifdef CL_THREAD_SAFE
 #include <pthread.h>
 pthread_mutex_t logg_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
0ae41a2d
 #ifdef  C_LINUX
 #include <libintl.h>
 #include <locale.h>
 
 #define gettext_noop(s) s
 #define _(s)    gettext(s)
 #define N_(s)   gettext_noop(s)
 
 #else
 
 #define _(s)    s
 #define N_(s)   s
 
 #endif
 
afff80ef
 FILE *logg_fp = NULL;
afb48b28
 
bcbe6ad7
 short int logg_verbose = 0, logg_nowarn = 0, logg_lock = 1, logg_time = 0, logg_foreground = 1;
1b38f50d
 unsigned int logg_size = 0;
58bcf502
 const char *logg_file = NULL;
 #if defined(USE_SYSLOG) && !defined(C_AIX)
0ae41a2d
 short logg_syslog;
58bcf502
 #endif
 
 short int mprintf_disabled = 0, mprintf_verbose = 0, mprintf_quiet = 0,
0378a9ab
 	  mprintf_stdout = 0, mprintf_nowarn = 0, mprintf_send_timeout = 100;
58bcf502
 
afff80ef
 #define ARGLEN(args, str, len)			    \
 {						    \
c71a0353
 	size_t arglen = 0, i;			    \
afff80ef
 	char *pt;				    \
     va_start(args, str);			    \
     len = strlen(str);				    \
     for(i = 0; i < len - 1; i++) {		    \
 	if(str[i] == '%') {			    \
 	    switch(str[++i]) {			    \
 		case 's':			    \
 		    pt  = va_arg(args, char *);	    \
 		    if(pt)			    \
 			arglen += strlen(pt);	    \
 		    break;			    \
6870ebe1
 		case 'f':			    \
 		    va_arg(args, double);	    \
 		    arglen += 25;		    \
 		    break;			    \
 		case 'l':			    \
 		    va_arg(args, long);		    \
 		    arglen += 20;		    \
 		    break;			    \
afff80ef
 		default:			    \
 		    va_arg(args, int);		    \
 		    arglen += 10;		    \
 		    break;			    \
 	    }					    \
 	}					    \
     }						    \
     va_end(args);				    \
     len += arglen;				    \
 }
 
afb48b28
 int mdprintf(int desc, const char *str, ...)
 {
 	va_list args;
afff80ef
 	char buffer[512], *abuffer = NULL, *buff;
c71a0353
 	int bytes, todo, ret=0;
 	size_t len;
afff80ef
 
 
     ARGLEN(args, str, len);
     if(len <= sizeof(buffer)) {
 	len = sizeof(buffer);
 	buff = buffer;
     } else {
 	abuffer = malloc(len);
 	if(!abuffer) {
 	    len = sizeof(buffer);
 	    buff = buffer;
 	} else {
 	    buff = abuffer;
 	}
     }
afb48b28
     va_start(args, str);
afff80ef
     bytes = vsnprintf(buff, len, str, args);
afb48b28
     va_end(args);
afff80ef
     buff[len - 1] = 0;
2e81b220
 
afff80ef
     if(bytes < 0) {
 	if(len > sizeof(buffer))
 	    free(abuffer);
2e81b220
 	return bytes;
afff80ef
     }
     if((size_t) bytes >= len)
 	bytes = len - 1;
 
0378a9ab
     todo = bytes;
     while (todo > 0) {
 	ret = send(desc, buff, bytes, 0);
 	if (ret < 0) {
 	    struct timeval tv;
 	    if (errno != EWOULDBLOCK)
 		break;
 	    tv.tv_sec = 0;
 	    tv.tv_usec = mprintf_send_timeout*1000;
 	    do {
 		fd_set wfds;
 		FD_ZERO(&wfds);
 		FD_SET(desc, &wfds);
 		ret = select(desc+1, NULL, &wfds, NULL, &tv);
 	    } while (ret < 0 && errno == EINTR);
 	    if (!ret) {
 		/* timed out */
 		ret = -1;
 		break;
 	    }
 	} else {
 	    todo -= ret;
 	    buff += ret;
 	}
     }
2e81b220
 
afff80ef
     if(len > sizeof(buffer))
 	free(abuffer);
2e81b220
 
0378a9ab
     return ret < 0 ? -1 : bytes;
afb48b28
 }
 
afff80ef
 void logg_close(void)
 {
 #if defined(USE_SYSLOG) && !defined(C_AIX)
     if(logg_syslog)
     	closelog();
 #endif
afb48b28
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&logg_mutex);
 #endif
afff80ef
     if(logg_fp) {
 	fclose(logg_fp);
 	logg_fp = NULL;
afb48b28
     }
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&logg_mutex);
 #endif
 }
 
2cf89f22
 /*
  * legend:
  *  ! - ERROR:
  *  ^ - WARNING:
  *  ~ - normal
  *  # - normal, not foreground (logfile and syslog only)
  *  * - verbose
  *  $ - debug
  *  none - normal
  *
  *	Default  Foreground LogVerbose Debug  Syslog
  *  !	  yes	   mprintf     yes      yes   LOG_ERR
  *  ^	  yes	   mprintf     yes	yes   LOG_WARNING
  *  ~	  yes	   mprintf     yes	yes   LOG_INFO
  *  #	  yes	     no	       yes	yes   LOG_INFO
  *  *	  no	   mprintf     yes	yes   LOG_DEBUG
  *  $	  no	   mprintf     no	yes   LOG_DEBUG
  *  none  yes	   mprintf     yes	yes   LOG_INFO
  */
afb48b28
 int logg(const char *str, ...)
 {
5a3aeff4
 	va_list args;
ace24e1f
 #ifdef F_WRLCK
afb48b28
 	struct flock fl;
ace24e1f
 #endif
afff80ef
 	char buffer[1025], *abuffer = NULL, *buff;
afb48b28
 	time_t currtime;
 	struct stat sb;
 	mode_t old_umask;
c71a0353
 	size_t len;
afff80ef
 
fb6fe4f5
     if ((*str == '$' && logg_verbose < 2) ||
 	(*str == '*' && !logg_verbose))
 	return 0;
afff80ef
 
     ARGLEN(args, str, len);
     if(len <= sizeof(buffer)) {
 	len = sizeof(buffer);
 	buff = buffer;
     } else {
 	abuffer = malloc(len);
 	if(!abuffer) {
 	    len = sizeof(buffer);
 	    buff = buffer;
 	} else {
 	    buff = abuffer;
 	}
     }
fe7ee98f
     va_start(args, str);
afff80ef
     vsnprintf(buff, len, str, args);
5a3aeff4
     va_end(args);
afff80ef
     buff[len - 1] = 0;
fe7ee98f
 
afb48b28
 #ifdef CL_THREAD_SAFE
234582ae
     pthread_mutex_lock(&logg_mutex);
afb48b28
 #endif
234582ae
     if(logg_file) {
afff80ef
 	if(!logg_fp) {
afb48b28
 	    old_umask = umask(0037);
afff80ef
 	    if((logg_fp = fopen(logg_file, "at")) == NULL) {
afb48b28
 		umask(old_umask);
 #ifdef CL_THREAD_SAFE
 		pthread_mutex_unlock(&logg_mutex);
 #endif
ce99b521
 		printf("ERROR: Can't open %s in append mode (check permissions!).\n", logg_file);
afff80ef
 		if(len > sizeof(buffer))
 		    free(abuffer);
afb48b28
 		return -1;
 	    } else umask(old_umask);
 
ace24e1f
 #ifdef F_WRLCK
afb48b28
 	    if(logg_lock) {
 		memset(&fl, 0, sizeof(fl));
 		fl.l_type = F_WRLCK;
afff80ef
 		if(fcntl(fileno(logg_fp), F_SETLK, &fl) == -1) {
afb48b28
 #ifdef CL_THREAD_SAFE
 		    pthread_mutex_unlock(&logg_mutex);
 #endif
c6677c94
 		    printf("ERROR: %s is locked by another process\n", logg_file);
afff80ef
 		    if(len > sizeof(buffer))
 			free(abuffer);
afb48b28
 		    return -1;
 		}
 	    }
ace24e1f
 #endif
afb48b28
 	}
 
 	if(logg_size) {
 	    if(stat(logg_file, &sb) != -1) {
73b243dc
 		if((unsigned int) sb.st_size > logg_size) {
afb48b28
 		    logg_file = NULL;
afff80ef
 		    fprintf(logg_fp, "Log size = %u, max = %u\n", (unsigned int) sb.st_size, logg_size);
 		    fprintf(logg_fp, "LOGGING DISABLED (Maximal log file size exceeded).\n");
 		    fclose(logg_fp);
 		    logg_fp = NULL;
afb48b28
 		}
 	    }
 	}
 
afff80ef
 	if(logg_fp) {
eff613a9
             /* Need to avoid logging time for verbose messages when logverbose
                is not set or we get a bunch of timestamps in the log without
                newlines... */
afff80ef
 	    if(logg_time && ((*buff != '*') || logg_verbose)) {
9e751804
 	        char timestr[32];
eff613a9
 		time(&currtime);
9e751804
 		cli_ctime(&currtime, timestr, sizeof(timestr));
 		/* cut trailing \n */
 		timestr[strlen(timestr)-1] = '\0';
afff80ef
 		fprintf(logg_fp, "%s -> ", timestr);
eff613a9
 	    }
afb48b28
 
afff80ef
 	    if(*buff == '!') {
 		fprintf(logg_fp, "ERROR: %s", buff + 1);
 	    } else if(*buff == '^') {
5a3aeff4
 		if(!logg_nowarn)
afff80ef
 		    fprintf(logg_fp, "WARNING: %s", buff + 1);
fb6fe4f5
 	    } else if(*buff == '*' || *buff == '$') {
afff80ef
 		    fprintf(logg_fp, "%s", buff + 1);
 	    } else if(*buff == '#' || *buff == '~') {
 		fprintf(logg_fp, "%s", buff + 1);
5a3aeff4
 	    } else
afff80ef
 		fprintf(logg_fp, "%s", buff);
afb48b28
 
afff80ef
 	    fflush(logg_fp);
eff613a9
 	}
20de3381
     }
afb48b28
 
 #if defined(USE_SYSLOG) && !defined(C_AIX)
     if(logg_syslog) {
afff80ef
 	if(buff[0] == '!') {
 	    syslog(LOG_ERR, "%s", buff + 1);
 	} else if(buff[0] == '^') {
bcbe6ad7
 	    if(!logg_nowarn)
afff80ef
 		syslog(LOG_WARNING, "%s", buff + 1);
fb6fe4f5
 	} else if(buff[0] == '*' || buff[0] == '$') {
 	    syslog(LOG_DEBUG, "%s", buff + 1);
afff80ef
 	} else if(buff[0] == '#' || buff[0] == '~') {
 	    syslog(LOG_INFO, "%s", buff + 1);
 	} else syslog(LOG_INFO, "%s", buff);
afb48b28
 
     }
 #endif
 
7b8edc5c
     if(logg_foreground) {
afff80ef
 	if(buff[0] != '#')
 	    mprintf("%s", buff);
0ae41a2d
     }
 
234582ae
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&logg_mutex);
 #endif
 
afff80ef
     if(len > sizeof(buffer))
 	free(abuffer);
afb48b28
     return 0;
 }
 
 void mprintf(const char *str, ...)
 {
0ae41a2d
 	va_list args;
afb48b28
 	FILE *fd;
afff80ef
 	char buffer[512], *abuffer = NULL, *buff;
c71a0353
 	size_t len;
afb48b28
 
 
0ae41a2d
     if(mprintf_disabled) 
afb48b28
 	return;
 
e1187134
     fd = stdout;
afb48b28
 
 /* legend:
  * ! - error
  * @ - error with logging
  * ...
  */
 
 /*
  *             ERROR    WARNING    STANDARD
e1187134
  * normal      stderr   stderr     stdout
afb48b28
  * 
e1187134
  * verbose     stderr   stderr     stdout
afb48b28
  * 
e1187134
  * quiet       stderr     no         no
afb48b28
  */
 
afff80ef
     ARGLEN(args, str, len);
     if(len <= sizeof(buffer)) {
 	len = sizeof(buffer);
 	buff = buffer;
     } else {
 	abuffer = malloc(len);
 	if(!abuffer) {
 	    len = sizeof(buffer);
 	    buff = buffer;
 	} else {
 	    buff = abuffer;
 	}
     }
afb48b28
     va_start(args, str);
afff80ef
     vsnprintf(buff, len, str, args);
118920f8
     va_end(args);
afff80ef
     buff[len - 1] = 0;
afb48b28
 
4b25a0de
     if(buff[0] == '!') {
e1187134
        if(!mprintf_stdout)
            fd = stderr;
118920f8
 	fprintf(fd, "ERROR: %s", &buff[1]);
4b25a0de
     } else if(buff[0] == '@') {
e1187134
        if(!mprintf_stdout)
            fd = stderr;
118920f8
 	fprintf(fd, "ERROR: %s", &buff[1]);
afb48b28
     } else if(!mprintf_quiet) {
4b25a0de
 	if(buff[0] == '^') {
bcbe6ad7
 	    if(!mprintf_nowarn) {
 		if(!mprintf_stdout)
 		    fd = stderr;
 		fprintf(fd, "WARNING: %s", &buff[1]);
 	    }
4b25a0de
 	} else if(buff[0] == '*') {
afb48b28
 	    if(mprintf_verbose)
118920f8
 		fprintf(fd, "%s", &buff[1]);
5a3aeff4
 	} else if(buff[0] == '~') {
 	    fprintf(fd, "%s", &buff[1]);
118920f8
 	} else fprintf(fd, "%s", buff);
afb48b28
     }
 
     if(fd == stdout)
 	fflush(stdout);
afff80ef
 
     if(len > sizeof(buffer))
 	free(abuffer);
afb48b28
 }
c695dab4
 
 struct facstruct {
     const char *name;
     int code;
 };
 
 #if defined(USE_SYSLOG) && !defined(C_AIX)
 static const struct facstruct facilitymap[] = {
45ae238c
 #ifdef LOG_AUTH
c695dab4
     { "LOG_AUTH",	LOG_AUTH },
45ae238c
 #endif
 #ifdef LOG_AUTHPRIV
c695dab4
     { "LOG_AUTHPRIV",	LOG_AUTHPRIV },
45ae238c
 #endif
 #ifdef LOG_CRON
c695dab4
     { "LOG_CRON",	LOG_CRON },
45ae238c
 #endif
 #ifdef LOG_DAEMON
c695dab4
     { "LOG_DAEMON",	LOG_DAEMON },
45ae238c
 #endif
 #ifdef LOG_FTP
c695dab4
     { "LOG_FTP",	LOG_FTP },
45ae238c
 #endif
 #ifdef LOG_KERN
c695dab4
     { "LOG_KERN",	LOG_KERN },
45ae238c
 #endif
 #ifdef LOG_LPR
c695dab4
     { "LOG_LPR",	LOG_LPR },
45ae238c
 #endif
 #ifdef LOG_MAIL
c695dab4
     { "LOG_MAIL",	LOG_MAIL },
45ae238c
 #endif
 #ifdef LOG_NEWS
c695dab4
     { "LOG_NEWS",	LOG_NEWS },
45ae238c
 #endif
 #ifdef LOG_AUTH
c695dab4
     { "LOG_AUTH",	LOG_AUTH },
45ae238c
 #endif
 #ifdef LOG_SYSLOG
c695dab4
     { "LOG_SYSLOG",	LOG_SYSLOG },
45ae238c
 #endif
 #ifdef LOG_USER
c695dab4
     { "LOG_USER",	LOG_USER },
45ae238c
 #endif
 #ifdef LOG_UUCP
c695dab4
     { "LOG_UUCP",	LOG_UUCP },
45ae238c
 #endif
 #ifdef LOG_LOCAL0
c695dab4
     { "LOG_LOCAL0",	LOG_LOCAL0 },
45ae238c
 #endif
 #ifdef LOG_LOCAL1
c695dab4
     { "LOG_LOCAL1",	LOG_LOCAL1 },
45ae238c
 #endif
 #ifdef LOG_LOCAL2
c695dab4
     { "LOG_LOCAL2",	LOG_LOCAL2 },
45ae238c
 #endif
 #ifdef LOG_LOCAL3
c695dab4
     { "LOG_LOCAL3",	LOG_LOCAL3 },
45ae238c
 #endif
 #ifdef LOG_LOCAL4
c695dab4
     { "LOG_LOCAL4",	LOG_LOCAL4 },
45ae238c
 #endif
 #ifdef LOG_LOCAL5
c695dab4
     { "LOG_LOCAL5",	LOG_LOCAL5 },
45ae238c
 #endif
 #ifdef LOG_LOCAL6
c695dab4
     { "LOG_LOCAL6",	LOG_LOCAL6 },
45ae238c
 #endif
 #ifdef LOG_LOCAL7
c695dab4
     { "LOG_LOCAL7",	LOG_LOCAL7 },
45ae238c
 #endif
c695dab4
     { NULL,		-1 }
 };
 
 int logg_facility(const char *name)
 {
 	int i;
 
     for(i = 0; facilitymap[i].name; i++)
 	if(!strcmp(facilitymap[i].name, name))
 	    return facilitymap[i].code;
 
     return -1;
 }
 #endif