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 |
*/ |
ace24e1f |
#ifdef _MSC_VER
#include <windows.h>
#include <winsock.h>
#endif
|
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> |
ace24e1f |
#ifndef C_WINDOWS |
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) \
{ \
int arglen = 0, i; \
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; |
025243f6 |
int bytes, todo, len, ret=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;
}
} |
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
}
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; |
afff80ef |
int len;
|
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;
int 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 |