afb48b28 |
/* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc. |
086eab5c |
*
* 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" |
944d7c82 |
#include "libclamav/clamav.h" |
9e751804 |
#include "libclamav/others.h" |
13d52e4f |
#include "libclamav/str.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; |
3d8a490c |
pthread_mutex_t mdprintf_mutex = PTHREAD_MUTEX_INITIALIZER; |
afb48b28 |
#endif
|
57c410e9 |
#if defined(C_LINUX) && defined(HAVE_LIBINTL_H) |
0ae41a2d |
#include <libintl.h>
#include <locale.h>
#define gettext_noop(s) s |
dc7f716a |
#define _(s) gettext(s)
#define N_(s) gettext_noop(s) |
0ae41a2d |
#else
|
dc7f716a |
#define _(s) s
#define N_(s) s |
0ae41a2d |
#endif
|
afff80ef |
FILE *logg_fp = NULL; |
afb48b28 |
|
42ccf9c2 |
short int logg_verbose = 0, logg_nowarn = 0, logg_lock = 1, logg_time = 0, logg_foreground = 1, logg_noflush = 0, logg_rotate = 0;
off_t 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, |
dc7f716a |
mprintf_stdout = 0, mprintf_nowarn = 0, mprintf_send_timeout = 100, mprintf_progress = 0;
#define ARGLEN(args, str, len) \
{ \
size_t arglen = 1, 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; \
case 'f': \
va_arg(args, double); \
arglen += 25; \
break; \
case 'l': \
va_arg(args, long); \
arglen += 20; \
break; \
default: \
va_arg(args, int); \
arglen += 10; \
break; \
} \
} \
} \
va_end(args); \
len += arglen; \
} |
afff80ef |
|
afb48b28 |
int mdprintf(int desc, const char *str, ...)
{ |
dc7f716a |
va_list args;
char buffer[512], *abuffer = NULL, *buff;
int bytes, todo, ret = 0;
size_t len; |
afff80ef |
ARGLEN(args, str, len); |
dc7f716a |
if (len <= sizeof(buffer))
{
len = sizeof(buffer);
buff = buffer;
}
else
{
abuffer = malloc(len);
if (!abuffer)
{
len = sizeof(buffer);
buff = buffer;
}
else
{
buff = abuffer;
} |
afff80ef |
} |
afb48b28 |
va_start(args, str); |
afff80ef |
bytes = vsnprintf(buff, len, str, args); |
afb48b28 |
va_end(args); |
afff80ef |
buff[len - 1] = 0; |
2e81b220 |
|
dc7f716a |
if (bytes < 0)
{
if (len > sizeof(buffer))
free(abuffer);
return bytes; |
afff80ef |
} |
dc7f716a |
if ((size_t)bytes >= len)
bytes = len - 1; |
afff80ef |
|
0378a9ab |
todo = bytes; |
3d8a490c |
#ifdef CL_THREAD_SAFE
/* make sure we don't mix sends from multiple threads,
* important for IDSESSION */
pthread_mutex_lock(&mdprintf_mutex);
#endif |
dc7f716a |
while (todo > 0)
{
ret = send(desc, buff, bytes, 0);
if (ret < 0)
{
struct timeval tv;
if (errno != EWOULDBLOCK)
break;
/* didn't send anything yet */ |
3d8a490c |
#ifdef CL_THREAD_SAFE |
dc7f716a |
pthread_mutex_unlock(&mdprintf_mutex);
#endif
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); |
3d8a490c |
#ifdef CL_THREAD_SAFE |
dc7f716a |
pthread_mutex_lock(&mdprintf_mutex);
#endif
if (!ret)
{
/* timed out */
ret = -1;
break;
}
}
else
{
todo -= ret;
buff += ret;
} |
0378a9ab |
} |
3d8a490c |
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&mdprintf_mutex);
#endif |
2e81b220 |
|
dc7f716a |
if (len > sizeof(buffer))
free(abuffer); |
2e81b220 |
|
0378a9ab |
return ret < 0 ? -1 : bytes; |
afb48b28 |
}
|
42ccf9c2 |
static int rename_logg(STATBUF *sb)
{
char *rotate_file;
size_t rotate_file_len;
time_t t;
struct tm tmp;
|
dc7f716a |
if (!logg_rotate)
{
if (logg_fp)
{ |
059ca614 |
fprintf(logg_fp, "Log size = %lld, max = %lld\n", (long long int)sb->st_size, (long long int)logg_size); |
e1c3ee76 |
fprintf(logg_fp, "WARNING: Log size limit met but log file rotation turned off. Forcing log file rotation anyways.\n"); |
ec039b6a |
} |
42ccf9c2 |
|
e1c3ee76 |
logg_rotate = 1; |
42ccf9c2 |
}
rotate_file_len = strlen(logg_file) + sizeof("-YYYY-MM-DD_HH:MM:SS");
rotate_file = calloc(1, rotate_file_len + 1); |
dc7f716a |
if (!rotate_file)
{ |
e1c3ee76 |
if (logg_fp) |
42ccf9c2 |
fprintf(logg_fp, "Need to rotate log file due to size but ran out of memory.\n");
return -1;
}
t = time(NULL); |
dc7f716a |
if (!localtime_r(&t, &tmp))
{ |
e1c3ee76 |
if (logg_fp) |
42ccf9c2 |
fprintf(logg_fp, "Need to rotate log file due to size but could not get local time.\n");
free(rotate_file);
return -1;
}
strcpy(rotate_file, logg_file); |
dc7f716a |
strftime(rotate_file + strlen(rotate_file), rotate_file_len - strlen(rotate_file), "-%Y%m%d_%H%M%S", &tmp); |
42ccf9c2 |
|
dc7f716a |
if (logg_fp)
{ |
42ccf9c2 |
fclose(logg_fp);
logg_fp = NULL;
}
|
dc7f716a |
if (rename(logg_file, rotate_file))
{ |
42ccf9c2 |
free(rotate_file);
return -1;
}
free(rotate_file);
return 0;
}
static int logg_open(void)
{
STATBUF sb;
|
dc7f716a |
if (logg_file)
if (logg_size > 0)
if (CLAMSTAT(logg_file, &sb) != -1)
if (sb.st_size > logg_size)
if (rename_logg(&sb)) |
42ccf9c2 |
return -1;
return 0;
}
|
afff80ef |
void logg_close(void)
{
#if defined(USE_SYSLOG) && !defined(C_AIX) |
dc7f716a |
if (logg_syslog)
closelog(); |
afff80ef |
#endif |
afb48b28 |
#ifdef CL_THREAD_SAFE
pthread_mutex_lock(&logg_mutex);
#endif |
dc7f716a |
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, ...)
{ |
dc7f716a |
va_list args;
char buffer[1025], *abuffer = NULL, *buff;
time_t currtime;
size_t len;
mode_t old_umask; |
42ccf9c2 |
#ifdef F_WRLCK |
dc7f716a |
struct flock fl; |
42ccf9c2 |
#endif |
afff80ef |
|
fb6fe4f5 |
if ((*str == '$' && logg_verbose < 2) || |
dc7f716a |
(*str == '*' && !logg_verbose))
return 0; |
afff80ef |
ARGLEN(args, str, len); |
dc7f716a |
if (len <= sizeof(buffer))
{
len = sizeof(buffer);
buff = buffer;
}
else
{
abuffer = malloc(len);
if (!abuffer)
{
len = sizeof(buffer);
buff = buffer;
}
else
{
buff = abuffer;
} |
afff80ef |
} |
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 |
42ccf9c2 |
logg_open();
|
dc7f716a |
if (!logg_fp && logg_file)
{ |
42ccf9c2 |
old_umask = umask(0037); |
dc7f716a |
if ((logg_fp = fopen(logg_file, "at")) == NULL)
{ |
42ccf9c2 |
umask(old_umask); |
afb48b28 |
#ifdef CL_THREAD_SAFE |
42ccf9c2 |
pthread_mutex_unlock(&logg_mutex); |
afb48b28 |
#endif |
42ccf9c2 |
printf("ERROR: Can't open %s in append mode (check permissions!).\n", logg_file); |
dc7f716a |
if (len > sizeof(buffer)) |
42ccf9c2 |
free(abuffer);
return -1; |
dc7f716a |
}
else
umask(old_umask); |
afb48b28 |
|
ace24e1f |
#ifdef F_WRLCK |
dc7f716a |
if (logg_lock)
{ |
42ccf9c2 |
memset(&fl, 0, sizeof(fl));
fl.l_type = F_WRLCK; |
dc7f716a |
if (fcntl(fileno(logg_fp), F_SETLK, &fl) == -1)
{ |
74f0e79e |
#ifdef EOPNOTSUPP |
dc7f716a |
if (errno == EOPNOTSUPP) |
42ccf9c2 |
printf("WARNING: File locking not supported (NFS?)\n");
else |
74f0e79e |
#endif |
42ccf9c2 |
{ |
afb48b28 |
#ifdef CL_THREAD_SAFE |
42ccf9c2 |
pthread_mutex_unlock(&logg_mutex); |
afb48b28 |
#endif |
42ccf9c2 |
printf("ERROR: %s is locked by another process\n", logg_file); |
dc7f716a |
if (len > sizeof(buffer))
free(abuffer); |
42ccf9c2 |
return -1;
}
}
} |
ace24e1f |
#endif |
42ccf9c2 |
} |
afb48b28 |
|
dc7f716a |
if (logg_fp)
{
char flush = !logg_noflush;
/* Need to avoid logging time for verbose messages when logverbose |
eff613a9 |
is not set or we get a bunch of timestamps in the log without
newlines... */ |
dc7f716a |
if (logg_time && ((*buff != '*') || logg_verbose))
{
char timestr[32];
time(&currtime);
cli_ctime(&currtime, timestr, sizeof(timestr));
/* cut trailing \n */
timestr[strlen(timestr) - 1] = '\0';
fprintf(logg_fp, "%s -> ", timestr);
} |
13d52e4f |
|
dc7f716a |
if (*buff == '!')
{
fprintf(logg_fp, "ERROR: %s", buff + 1);
flush = 1;
}
else if (*buff == '^')
{
if (!logg_nowarn)
fprintf(logg_fp, "WARNING: %s", buff + 1);
flush = 1;
}
else if (*buff == '*' || *buff == '$')
{
fprintf(logg_fp, "%s", buff + 1);
}
else if (*buff == '#' || *buff == '~')
{
fprintf(logg_fp, "%s", buff + 1);
}
else
fprintf(logg_fp, "%s", buff); |
afb48b28 |
|
dc7f716a |
if (flush)
fflush(logg_fp);
}
if (logg_foreground)
{
if (buff[0] != '#')
{
if (logg_time)
{
char timestr[32];
time(&currtime);
cli_ctime(&currtime, timestr, sizeof(timestr));
/* cut trailing \n */
timestr[strlen(timestr) - 1] = '\0';
mprintf("%s -> %s", timestr, buff);
}
else
{
mprintf("%s", buff);
}
}
}
#if defined(USE_SYSLOG) && !defined(C_AIX)
if (logg_syslog)
{
cli_chomp(buff);
if (buff[0] == '!')
{
syslog(LOG_ERR, "%s", buff + 1);
}
else if (buff[0] == '^')
{
if (!logg_nowarn)
syslog(LOG_WARNING, "%s", buff + 1);
}
else if (buff[0] == '*' || buff[0] == '$')
{
syslog(LOG_DEBUG, "%s", buff + 1);
}
else if (buff[0] == '#' || buff[0] == '~')
{
syslog(LOG_INFO, "%s", buff + 1);
}
else
syslog(LOG_INFO, "%s", buff); |
afb48b28 |
}
#endif
|
234582ae |
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&logg_mutex);
#endif
|
dc7f716a |
if (len > sizeof(buffer))
free(abuffer); |
afb48b28 |
return 0;
}
void mprintf(const char *str, ...)
{ |
dc7f716a |
va_list args;
FILE *fd;
char buffer[512], *abuffer = NULL, *buff;
size_t len; |
afb48b28 |
|
dc7f716a |
if (mprintf_disabled)
return; |
afb48b28 |
|
e1187134 |
fd = stdout; |
afb48b28 |
|
dc7f716a |
/* legend: |
afb48b28 |
* ! - error
* @ - error with logging
* ...
*/
|
dc7f716a |
/* |
afb48b28 |
* 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); |
dc7f716a |
if (len <= sizeof(buffer))
{
len = sizeof(buffer);
buff = buffer;
}
else
{
abuffer = malloc(len);
if (!abuffer)
{
len = sizeof(buffer);
buff = buffer;
}
else
{
buff = abuffer;
} |
afff80ef |
} |
afb48b28 |
va_start(args, str); |
afff80ef |
vsnprintf(buff, len, str, args); |
118920f8 |
va_end(args); |
afff80ef |
buff[len - 1] = 0; |
afb48b28 |
|
254cecef |
#ifdef _WIN32 |
dc7f716a |
do
{
int tmplen = len + 1;
wchar_t *tmpw = malloc(tmplen * sizeof(wchar_t));
char *nubuff;
if (!tmpw)
break;
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buff, -1, tmpw, tmplen))
{
free(tmpw);
break;
}
/* FIXME CHECK IT'S REALLY UTF8 */
nubuff = (char *)malloc(tmplen);
if (!nubuff)
{
free(tmpw);
break;
}
if (!WideCharToMultiByte(CP_OEMCP, 0, tmpw, -1, nubuff, tmplen, NULL, NULL))
{
free(nubuff);
free(tmpw);
break;
}
free(tmpw);
if (len > sizeof(buffer))
free(abuffer);
abuffer = buff = nubuff;
len = sizeof(buffer) + 1;
} while (0);
#endif
if (buff[0] == '!')
{
if (!mprintf_stdout)
fd = stderr;
fprintf(fd, "ERROR: %s", &buff[1]);
}
else if (buff[0] == '@')
{
if (!mprintf_stdout)
fd = stderr;
fprintf(fd, "ERROR: %s", &buff[1]);
}
else if (!mprintf_quiet)
{
if (buff[0] == '^')
{
if (!mprintf_nowarn)
{
if (!mprintf_stdout)
fd = stderr;
fprintf(fd, "WARNING: %s", &buff[1]);
}
}
else if (buff[0] == '*')
{
if (mprintf_verbose)
fprintf(fd, "%s", &buff[1]);
}
else if (buff[0] == '~')
{
fprintf(fd, "%s", &buff[1]);
}
else
fprintf(fd, "%s", buff); |
afb48b28 |
}
|
dc7f716a |
if (fd == stdout)
fflush(stdout); |
afff80ef |
|
dc7f716a |
if (len > sizeof(buffer))
free(abuffer); |
afb48b28 |
} |
c695dab4 |
|
dc7f716a |
struct facstruct
{ |
c695dab4 |
const char *name;
int code;
};
#if defined(USE_SYSLOG) && !defined(C_AIX)
static const struct facstruct facilitymap[] = { |
45ae238c |
#ifdef LOG_AUTH |
dc7f716a |
{"LOG_AUTH", LOG_AUTH}, |
45ae238c |
#endif
#ifdef LOG_AUTHPRIV |
dc7f716a |
{"LOG_AUTHPRIV", LOG_AUTHPRIV}, |
45ae238c |
#endif
#ifdef LOG_CRON |
dc7f716a |
{"LOG_CRON", LOG_CRON}, |
45ae238c |
#endif
#ifdef LOG_DAEMON |
dc7f716a |
{"LOG_DAEMON", LOG_DAEMON}, |
45ae238c |
#endif
#ifdef LOG_FTP |
dc7f716a |
{"LOG_FTP", LOG_FTP}, |
45ae238c |
#endif
#ifdef LOG_KERN |
dc7f716a |
{"LOG_KERN", LOG_KERN}, |
45ae238c |
#endif
#ifdef LOG_LPR |
dc7f716a |
{"LOG_LPR", LOG_LPR}, |
45ae238c |
#endif
#ifdef LOG_MAIL |
dc7f716a |
{"LOG_MAIL", LOG_MAIL}, |
45ae238c |
#endif
#ifdef LOG_NEWS |
dc7f716a |
{"LOG_NEWS", LOG_NEWS}, |
45ae238c |
#endif
#ifdef LOG_AUTH |
dc7f716a |
{"LOG_AUTH", LOG_AUTH}, |
45ae238c |
#endif
#ifdef LOG_SYSLOG |
dc7f716a |
{"LOG_SYSLOG", LOG_SYSLOG}, |
45ae238c |
#endif
#ifdef LOG_USER |
dc7f716a |
{"LOG_USER", LOG_USER}, |
45ae238c |
#endif
#ifdef LOG_UUCP |
dc7f716a |
{"LOG_UUCP", LOG_UUCP}, |
45ae238c |
#endif
#ifdef LOG_LOCAL0 |
dc7f716a |
{"LOG_LOCAL0", LOG_LOCAL0}, |
45ae238c |
#endif
#ifdef LOG_LOCAL1 |
dc7f716a |
{"LOG_LOCAL1", LOG_LOCAL1}, |
45ae238c |
#endif
#ifdef LOG_LOCAL2 |
dc7f716a |
{"LOG_LOCAL2", LOG_LOCAL2}, |
45ae238c |
#endif
#ifdef LOG_LOCAL3 |
dc7f716a |
{"LOG_LOCAL3", LOG_LOCAL3}, |
45ae238c |
#endif
#ifdef LOG_LOCAL4 |
dc7f716a |
{"LOG_LOCAL4", LOG_LOCAL4}, |
45ae238c |
#endif
#ifdef LOG_LOCAL5 |
dc7f716a |
{"LOG_LOCAL5", LOG_LOCAL5}, |
45ae238c |
#endif
#ifdef LOG_LOCAL6 |
dc7f716a |
{"LOG_LOCAL6", LOG_LOCAL6}, |
45ae238c |
#endif
#ifdef LOG_LOCAL7 |
dc7f716a |
{"LOG_LOCAL7", LOG_LOCAL7}, |
45ae238c |
#endif |
dc7f716a |
{NULL, -1}}; |
c695dab4 |
int logg_facility(const char *name)
{ |
dc7f716a |
int i; |
c695dab4 |
|
dc7f716a |
for (i = 0; facilitymap[i].name; i++)
if (!strcmp(facilitymap[i].name, name))
return facilitymap[i].code; |
c695dab4 |
return -1;
}
#endif |