zeek/src/util.cc
Robin Sommer 93894eed9b Overhauling the internal reporting of messages to the user.
The Logger class is now in charge of reporting all errors, warnings,
informational messages, weirds, and syslogs. All other components
route their messages through the global bro_logger singleton.

The Logger class comes with these reporting methods:

    void Message(const char* fmt, ...);
    void Warning(const char* fmt, ...);
    void Error(const char* fmt, ...);
    void FatalError(const char* fmt, ...); // Terminate Bro.
    void Weird(const char* name);
    [ .. some more Weird() variants ... ]
    void Syslog(const char* fmt, ...);
    void InternalWarning(const char* fmt, ...);
    void InternalError(const char* fmt, ...); // Terminates Bro.

See Logger.h for more information on these.

Generally, the reporting now works as follows:

    - All non-fatal message are reported in one of two ways:

        (1) At startup (i.e., before we start processing packets),
            they are logged to stderr.

        (2) During processing, they turn into events:

            event log_message%(msg: string, location: string%);
            event log_warning%(msg: string, location: string%);
            event log_error%(msg: string, location: string%);

            The script level can then handle them as desired.

            If we don't have an event handler, we fall back to
            reporting on stderr.

    - All fatal errors are logged to stderr and Bro terminates
      immediately.

    - Syslog(msg) directly syslogs, but doesn't do anything else.

The three main types of messages can also be generated on the
scripting layer via new Log::* bifs:

    Log::error(msg: string);
    Log::warning(msg: string);
    Log::message(msg: string);

These pass through the bro_logger as well and thus are handled in the
same way. Their output includes location information.

More changes:

    - Removed the alarm statement and the alarm_hook event.

    - Adapted lots of locations to use the bro_logger, including some
      of the messages that were previously either just written to
      stdout, or even funneled through the alarm mechanism.

    - No distinction anymore between Error() and RunTime(). There's
      now only one class of errors; the line was quite blurred already
      anyway.

    - util.h: all the error()/warn()/message()/run_time()/pinpoint()
      functions are gone. Use the bro_logger instead now.

    - Script errors are formatted a bit differently due to the
      changes. What I've seen so far looks ok to me, but let me know
      if there's something odd.

Notes:

    - The default handlers for the new log_* events are just dummy
      implementations for now since we need to integrate all this into
      the new scripts anyway.

    - I'm not too happy with the names of the Logger class and its
      instance bro_logger. We now have a LogMgr as well, which makes
      this all a bit confusing. But I didn't have a good idea for
      better names so I stuck with them for now.

      Perhaps we should merge Logger and LogMgr?
2011-06-25 16:40:54 -07:00

1193 lines
23 KiB
C++

// $Id: util.cc 6916 2009-09-24 20:48:36Z vern $
//
// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#ifdef HAVE_MALLINFO
# include <malloc.h>
#endif
#include "input.h"
#include "util.h"
#include "Obj.h"
#include "md5.h"
#include "Val.h"
#include "NetVar.h"
#include "Net.h"
#include "Logger.h"
char* copy_string(const char* s)
{
char* c = new char[strlen(s)+1];
strcpy(c, s);
return c;
}
int streq(const char* s1, const char* s2)
{
return ! strcmp(s1, s2);
}
int expand_escape(const char*& s)
{
switch ( *(s++) ) {
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'a': return '\a';
case 'v': return '\v';
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
{ // \<octal>{1,3}
--s; // put back the first octal digit
const char* start = s;
// Don't increment inside loop control
// because if isdigit() is a macro it might
// expand into multiple increments ...
// Here we define a maximum length for escape sequence
// to allow easy handling of string like: "^H0" as
// "\0100".
for ( int len = 0; len < 3 && isascii(*s) && isdigit(*s);
++s, ++len)
;
int result;
if ( sscanf(start, "%3o", &result) != 1 )
{
bro_logger->Warning("bad octal escape: ", start);
result = 0;
}
return result;
}
case 'x':
{ /* \x<hex> */
const char* start = s;
// Look at most 2 characters, so that "\x0ddir" -> "^Mdir".
for ( int len = 0; len < 2 && isascii(*s) && isxdigit(*s);
++s, ++len)
;
int result;
if ( sscanf(start, "%2x", &result) != 1 )
{
bro_logger->Warning("bad hexadecimal escape: ", start);
result = 0;
}
return result;
}
default:
return s[-1];
}
}
char* skip_whitespace(char* s)
{
while ( *s == ' ' || *s == '\t' )
++s;
return s;
}
const char* skip_whitespace(const char* s)
{
while ( *s == ' ' || *s == '\t' )
++s;
return s;
}
char* skip_whitespace(char* s, char* end_of_s)
{
while ( s < end_of_s && (*s == ' ' || *s == '\t') )
++s;
return s;
}
const char* skip_whitespace(const char* s, const char* end_of_s)
{
while ( s < end_of_s && (*s == ' ' || *s == '\t') )
++s;
return s;
}
char* skip_digits(char* s)
{
while ( *s && isdigit(*s) )
++s;
return s;
}
char* get_word(char*& s)
{
char* w = s;
while ( *s && ! isspace(*s) )
++s;
if ( *s )
{
*s = '\0'; // terminate the word
s = skip_whitespace(s+1);
}
return w;
}
void get_word(int length, const char* s, int& pwlen, const char*& pw)
{
pw = s;
int len = 0;
while ( len < length && *s && ! isspace(*s) )
{
++s;
++len;
}
pwlen = len;
}
void to_upper(char* s)
{
while ( *s )
{
if ( islower(*s) )
*s = toupper(*s);
++s;
}
}
const char* strchr_n(const char* s, const char* end_of_s, char ch)
{
for ( ; s < end_of_s; ++s )
if ( *s == ch )
return s;
return 0;
}
const char* strrchr_n(const char* s, const char* end_of_s, char ch)
{
for ( --end_of_s; end_of_s >= s; --end_of_s )
if ( *end_of_s == ch )
return end_of_s;
return 0;
}
int decode_hex(char ch)
{
if ( ch >= '0' && ch <= '9' )
return ch - '0';
if ( ch >= 'A' && ch <= 'F' )
return ch - 'A' + 10;
if ( ch >= 'a' && ch <= 'f' )
return ch - 'a' + 10;
return -1;
}
unsigned char encode_hex(int h)
{
static const char hex[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'
};
if ( h < 0 || h >= 16 )
{
bro_logger->InternalError("illegal value for encode_hex: %d", h);
return 'X';
}
return hex[h];
}
// Same as strpbrk except that s is not NUL-terminated, but limited by
// len. Note that '\0' is always implicitly contained in charset.
const char* strpbrk_n(size_t len, const char* s, const char* charset)
{
for ( const char* p = s; p < s + len; ++p )
if ( strchr(charset, *p) )
return p;
return 0;
}
int strcasecmp_n(int b_len, const char* b, const char* t)
{
if ( ! b )
return -1;
int i;
for ( i = 0; i < b_len; ++i )
{
char c1 = islower(b[i]) ? toupper(b[i]) : b[i];
char c2 = islower(t[i]) ? toupper(t[i]) : t[i];
if ( c1 < c2 )
return -1;
if ( c1 > c2 )
return 1;
}
return t[i] != '\0';
}
#ifndef HAVE_STRCASESTR
// This code is derived from software contributed to BSD by Chris Torek.
char* strcasestr(const char* s, const char* find)
{
char c = *find++;
if ( c )
{
c = tolower((unsigned char) c);
size_t len = strlen(find);
do {
char sc;
do {
sc = *s++;
if ( sc == 0 )
return 0;
} while ( char(tolower((unsigned char) sc)) != c );
} while ( strcasecmp_n(len, s, find) != 0 );
--s;
}
return (char*) s;
}
#endif
template<class T> int atoi_n(int len, const char* s, const char** end, int base, T& result)
{
T n = 0;
int neg = 0;
if ( len > 0 && *s == '-' )
{
neg = 1;
--len; ++s;
}
int i;
for ( i = 0; i < len; ++i )
{
unsigned int d;
if ( isdigit(s[i]) )
d = s[i] - '0';
else if ( s[i] >= 'a' && s[i] < 'a' - 10 + base )
d = s[i] - 'a' + 10;
else if ( s[i] >= 'A' && s[i] < 'A' - 10 + base )
d = s[i] - 'A' + 10;
else if ( i > 0 )
break;
else
return 0;
n = n * base + d;
}
if ( neg )
result = -n;
else
result = n;
if ( end )
*end = s + i;
return 1;
}
// Instantiate the ones we need.
template int atoi_n<int>(int len, const char* s, const char** end, int base, int& result);
template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result);
char* uitoa_n(uint64 value, char* str, int n, int base, const char* prefix)
{
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
assert(n);
int i = 0;
uint64 v;
char* p, *q;
char c;
if ( prefix )
{
strncpy(str, prefix, n);
str[n-1] = '\0';
i += strlen(prefix);
}
if ( i >= n )
return str;
v = value;
do {
str[i++] = dig[v % base];
v /= base;
} while ( v && i < n - 1 );
str[i] = '\0';
return str;
}
int strstr_n(const int big_len, const u_char* big,
const int little_len, const u_char* little)
{
if ( little_len > big_len )
return -1;
for ( int i = 0; i <= big_len - little_len; ++i )
{
if ( ! memcmp(big + i, little, little_len) )
return i;
}
return -1;
}
int fputs(int len, const char* s, FILE* fp)
{
for ( int i = 0; i < len; ++i )
if ( fputc(s[i], fp) == EOF )
return EOF;
return 0;
}
bool is_printable(const char* s, int len)
{
while ( --len >= 0 )
if ( ! isprint(*s++) )
return false;
return true;
}
const char* fmt_bytes(const char* data, int len)
{
static char buf[1024];
char* p = buf;
for ( int i = 0; i < len && p - buf < int(sizeof(buf)); ++i )
{
if ( isprint(data[i]) )
*p++ = data[i];
else
p += snprintf(p, sizeof(buf) - (p - buf),
"\\x%02x", (unsigned char) data[i]);
}
if ( p - buf < int(sizeof(buf)) )
*p = '\0';
else
buf[sizeof(buf) - 1] = '\0';
return buf;
}
const char* fmt(const char* format, ...)
{
static char* buf = 0;
static unsigned int buf_len = 1024;
if ( ! buf )
buf = (char*) malloc(buf_len);
va_list al;
va_start(al, format);
int n = safe_vsnprintf(buf, buf_len, format, al);
va_end(al);
if ( (unsigned int) n >= buf_len )
{ // Not enough room, grow the buffer.
buf_len = n + 32;
buf = (char*) realloc(buf, buf_len);
// Is it portable to restart?
va_start(al, format);
n = safe_vsnprintf(buf, buf_len, format, al);
va_end(al);
if ( (unsigned int) n >= buf_len )
bro_logger->InternalError("confusion reformatting in fmt()");
}
return buf;
}
const char* fmt_access_time(double t)
{
static char buf[256];
time_t time = (time_t) t;
strftime(buf, sizeof(buf), "%d/%m-%H:%M", localtime(&time));
return buf;
}
bool ensure_dir(const char *dirname)
{
struct stat st;
if ( stat(dirname, &st) < 0 )
{
if ( errno != ENOENT )
{
bro_logger->Warning(fmt("can't stat directory %s: %s",
dirname, strerror(errno)));
return false;
}
if ( mkdir(dirname, 0700) < 0 )
{
bro_logger->Warning(fmt("can't create directory %s: %s",
dirname, strerror(errno)));
return false;
}
}
else if ( ! S_ISDIR(st.st_mode) )
{
bro_logger->Warning(fmt("%s exists but is not a directory", dirname));
return false;
}
return true;
}
bool is_dir(const char* path)
{
struct stat st;
if ( stat(path, &st) < 0 )
{
if ( errno != ENOENT )
bro_logger->Warning(fmt("can't stat %s: %s", path, strerror(errno)));
return false;
}
return S_ISDIR(st.st_mode);
}
void hash_md5(size_t size, const unsigned char* bytes, unsigned char digest[16])
{
md5_state_s h;
md5_init(&h);
md5_append(&h, bytes, size);
md5_finish(&h, digest);
}
const char* md5_digest_print(const unsigned char digest[16])
{
static char digest_print[256];
for ( int i = 0; i < 16; ++i )
snprintf(digest_print + i * 2, 3, "%02x", digest[i]);
return digest_print;
}
int hmac_key_set = 0;
uint8 shared_hmac_md5_key[16];
void hmac_md5(size_t size, const unsigned char* bytes, unsigned char digest[16])
{
if ( ! hmac_key_set )
bro_logger->InternalError("HMAC-MD5 invoked before the HMAC key is set");
hash_md5(size, bytes, digest);
for ( int i = 0; i < 16; ++i )
digest[i] ^= shared_hmac_md5_key[i];
hash_md5(16, digest, digest);
}
static bool read_random_seeds(const char* read_file, uint32* seed,
uint32* buf, int bufsiz)
{
struct stat st;
FILE* f = 0;
if ( stat(read_file, &st) < 0 )
{
bro_logger->Warning(fmt("Seed file '%s' does not exist: %s",
read_file, strerror(errno)));
return false;
}
if ( ! (f = fopen(read_file, "r")) )
{
bro_logger->Warning(fmt("Could not open seed file '%s': %s",
read_file, strerror(errno)));
return false;
}
// Read seed for srandom().
if ( fscanf(f, "%u", seed) != 1 )
{
fclose(f);
return false;
}
// Read seeds for MD5.
for ( int i = 0; i < bufsiz; ++i )
{
int tmp;
if ( fscanf(f, "%u", &tmp) != 1 )
{
fclose(f);
return false;
}
buf[i] = tmp;
}
fclose(f);
return true;
}
static bool write_random_seeds(const char* write_file, uint32 seed,
uint32* buf, int bufsiz)
{
FILE* f = 0;
if ( ! (f = fopen(write_file, "w+")) )
{
bro_logger->Warning(fmt("Could not create seed file '%s': %s",
write_file, strerror(errno)));
return false;
}
fprintf(f, "%u\n", seed);
for ( int i = 0; i < bufsiz; ++i )
fprintf(f, "%u\n", buf[i]);
fclose(f);
return true;
}
static bool bro_rand_determistic = false;
static unsigned int bro_rand_state = 0;
static void bro_srand(unsigned int seed, bool deterministic)
{
bro_rand_state = seed;
bro_rand_determistic = deterministic;
srand(seed);
}
void init_random_seed(uint32 seed, const char* read_file, const char* write_file)
{
static const int bufsiz = 16;
uint32 buf[bufsiz];
int pos = 0; // accumulates entropy
bool seeds_done = false;
if ( read_file )
{
if ( ! read_random_seeds(read_file, &seed, buf, bufsiz) )
bro_logger->Error("Could not load seeds from file '%s'.\n",
read_file);
else
seeds_done = true;
}
if ( ! seeds_done )
{
// Gather up some entropy.
gettimeofday((struct timeval *)(buf + pos), 0);
pos += sizeof(struct timeval) / sizeof(uint32);
#if defined(O_NONBLOCK)
int fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
#elif defined(O_NDELAY)
int fd = open("/dev/random", O_RDONLY | O_NDELAY);
#else
int fd = open("/dev/random", O_RDONLY);
#endif
if ( fd >= 0 )
{
int amt = read(fd, buf + pos,
sizeof(uint32) * (bufsiz - pos));
close(fd);
if ( amt > 0 )
pos += amt / sizeof(uint32);
else
// Clear errno, which can be set on some
// systems due to a lack of entropy.
errno = 0;
}
if ( pos < bufsiz )
{
buf[pos++] = getpid();
if ( pos < bufsiz )
buf[pos++] = getuid();
}
if ( ! seed )
{
for ( int i = 0; i < pos; ++i )
{
seed ^= buf[i];
seed = (seed << 1) | (seed >> 31);
}
}
else
seeds_done = true;
}
bro_srand(seed, seeds_done);
if ( ! hmac_key_set )
{
hash_md5(sizeof(buf), (u_char*) buf, shared_hmac_md5_key);
hmac_key_set = 1;
}
if ( write_file && ! write_random_seeds(write_file, seed, buf, bufsiz) )
bro_logger->Error("Could not write seeds to file '%s'.\n",
write_file);
}
bool have_random_seed()
{
return bro_rand_determistic;
}
long int bro_random()
{
if ( ! bro_rand_determistic )
return random(); // Use system PRNG.
// Use our own simple linear congruence PRNG to make sure we are
// predictable across platforms.
const long int m = 2147483647;
const long int a = 16807;
const long int q = m / a;
const long int r = m % a;
bro_rand_state = a * ( bro_rand_state % q ) - r * ( bro_rand_state / q );
if ( bro_rand_state <= 0 )
bro_rand_state += m;
return bro_rand_state;
}
// Returns a 64-bit random string.
uint64 rand64bit()
{
uint64 base = 0;
int i;
for ( i = 1; i <= 4; ++i )
base = (base<<16) | bro_random();
return base;
}
int int_list_cmp(const void* v1, const void* v2)
{
ptr_compat_int i1 = *(ptr_compat_int*) v1;
ptr_compat_int i2 = *(ptr_compat_int*) v2;
if ( i1 < i2 )
return -1;
else if ( i1 == i2 )
return 0;
else
return 1;
}
const char* bro_path()
{
const char* path = getenv("BROPATH");
if ( ! path )
path = ".:"
POLICYDEST ":"
POLICYDEST "/sigs:"
POLICYDEST "/time-machine:"
POLICYDEST "/site";
return path;
}
const char* bro_prefixes()
{
int len = 1; // room for \0
loop_over_list(prefixes, i)
len += strlen(prefixes[i]) + 1;
char* p = new char[len];
loop_over_list(prefixes, j)
if ( j == 0 )
strcpy(p, prefixes[j]);
else
{
strcat(p, ":");
strcat(p, prefixes[j]);
}
return p;
}
static const char* PACKAGE_LOADER = "__load__.bro";
// If filename is pointing to a directory that contains a file called
// PACKAGE_LOADER, returns the files path. Otherwise returns filename itself.
// In both cases, the returned string is newly allocated.
static const char* check_for_dir(const char* filename, bool load_pkgs)
{
if ( load_pkgs && is_dir(filename) )
{
char init_filename_buf[1024];
safe_snprintf(init_filename_buf, sizeof(init_filename_buf),
"%s/%s", filename, PACKAGE_LOADER);
if ( access(init_filename_buf, R_OK) == 0 )
return copy_string(init_filename_buf);
}
return copy_string(filename);
}
FILE* open_file(const char* filename, const char** full_filename, bool load_pkgs)
{
filename = check_for_dir(filename, load_pkgs);
if ( full_filename )
*full_filename = copy_string(filename);
FILE* f = fopen(filename, "r");
delete [] filename;
return f;
}
FILE* search_for_file(const char* filename, const char* ext,
const char** full_filename, bool load_pkgs)
{
if ( filename[0] == '/' || filename[0] == '.' )
return open_file(filename, full_filename, load_pkgs);
char path[1024], full_filename_buf[1024];
safe_strncpy(path, bro_path(), sizeof(path));
char* dir_beginning = path;
char* dir_ending = path;
int more = *dir_beginning != '\0';
while ( more )
{
while ( *dir_ending && *dir_ending != ':' )
++dir_ending;
if ( *dir_ending == ':' )
*dir_ending = '\0';
else
more = 0;
safe_snprintf(full_filename_buf, sizeof(full_filename_buf),
"%s/%s.%s", dir_beginning, filename, ext);
if ( access(full_filename_buf, R_OK) == 0 &&
! is_dir(full_filename_buf) )
return open_file(full_filename_buf, full_filename, load_pkgs);
safe_snprintf(full_filename_buf, sizeof(full_filename_buf),
"%s/%s", dir_beginning, filename);
if ( access(full_filename_buf, R_OK) == 0 )
return open_file(full_filename_buf, full_filename, load_pkgs);
dir_beginning = ++dir_ending;
}
if ( full_filename )
*full_filename = copy_string(filename);
return 0;
}
FILE* rotate_file(const char* name, RecordVal* rotate_info)
{
// Build file names.
const int buflen = strlen(name) + 128;
char tmpname[buflen], newname[buflen+4];
safe_snprintf(newname, buflen, "%s.%d.%.06f.tmp",
name, getpid(), network_time);
newname[buflen-1] = '\0';
strcpy(tmpname, newname);
strcat(tmpname, ".tmp");
// First open the new file using a temporary name.
FILE* newf = fopen(tmpname, "w");
if ( ! newf )
{
bro_logger->Error(fmt("rotate_file: can't open %s: %s", tmpname, strerror(errno)));
return 0;
}
// Then move old file to "<name>.<pid>.<timestamp>" and make sure
// it really gets created.
struct stat dummy;
if ( link(name, newname) < 0 || stat(newname, &dummy) < 0 )
{
bro_logger->Error(fmt("rotate_file: can't move %s to %s: %s", name, newname, strerror(errno)));
fclose(newf);
unlink(newname);
unlink(tmpname);
return 0;
}
// Close current file, and move the tmp to its place.
if ( unlink(name) < 0 || link(tmpname, name) < 0 || unlink(tmpname) < 0 )
{
bro_logger->Error(fmt("rotate_file: can't move %s to %s: %s", tmpname, name, strerror(errno)));
exit(1); // hard to fix, but shouldn't happen anyway...
}
// Init rotate_info.
if ( rotate_info )
{
rotate_info->Assign(0, new StringVal(name));
rotate_info->Assign(1, new StringVal(newname));
rotate_info->Assign(2, new Val(network_time, TYPE_TIME));
rotate_info->Assign(3, new Val(network_time, TYPE_TIME));
}
return newf;
}
const char* log_file_name(const char* tag)
{
const char* env = getenv("BRO_LOG_SUFFIX");
return fmt("%s.%s", tag, (env ? env : "log"));
}
double calc_next_rotate(double interval, const char* rotate_base_time)
{
double current = network_time;
// Calculate start of day.
time_t teatime = time_t(current);
struct tm t;
t = *localtime(&teatime);
t.tm_hour = t.tm_min = t.tm_sec = 0;
double startofday = mktime(&t);
double base = -1;
if ( rotate_base_time && rotate_base_time[0] != '\0' )
{
struct tm t;
if ( ! strptime(rotate_base_time, "%H:%M", &t) )
bro_logger->Error("calc_next_rotate(): can't parse rotation base time");
else
base = t.tm_min * 60 + t.tm_hour * 60 * 60;
}
if ( base < 0 )
// No base time given. To get nice timestamps, we round
// the time up to the next multiple of the rotation interval.
return floor(current / interval) * interval
+ interval - current;
// current < startofday + base + i * interval <= current + interval
return startofday + base +
ceil((current - startofday - base) / interval) * interval -
current;
}
RETSIGTYPE sig_handler(int signo);
void terminate_processing()
{
if ( ! terminating )
sig_handler(SIGTERM);
}
extern const char* proc_status_file;
void _set_processing_status(const char* status)
{
if ( ! proc_status_file )
return;
// This function can be called from a signal context, so we have to
// make sure to only call reentrant functions and to restore errno
// afterwards.
int old_errno = errno;
int fd = open(proc_status_file, O_CREAT | O_WRONLY | O_TRUNC, 0700);
int len = strlen(status);
while ( len )
{
int n = write(fd, status, len);
if ( n < 0 && errno != EINTR && errno != EAGAIN )
// Ignore errors, as they're too difficult to
// safely report here.
break;
status += n;
len -= n;
}
close(fd);
errno = old_errno;
}
double current_time(bool real)
{
struct timeval tv;
if ( gettimeofday(&tv, 0) < 0 )
bro_logger->InternalError("gettimeofday failed in current_time()");
double t = double(tv.tv_sec) + double(tv.tv_usec) / 1e6;
if ( ! pseudo_realtime || real || pkt_srcs.length() == 0 )
return t;
// This obviously only works for a single source ...
PktSrc* src = pkt_srcs[0];
if ( net_is_processing_suspended() )
return src->CurrentPacketTimestamp();
// We don't scale with pseudo_realtime here as that would give us a
// jumping real-time.
return src->CurrentPacketTimestamp() +
(t - src->CurrentPacketWallClock());
}
struct timeval double_to_timeval(double t)
{
struct timeval tv;
double t1 = floor(t);
tv.tv_sec = int(t1);
tv.tv_usec = int((t - t1) * 1e6 + 0.5);
return tv;
}
int time_compare(struct timeval* tv_a, struct timeval* tv_b)
{
if ( tv_a->tv_sec == tv_b->tv_sec )
return tv_a->tv_usec - tv_b->tv_usec;
else
return tv_a->tv_sec - tv_b->tv_sec;
}
static uint64 uid_counter; // Counter for unique IDs.
static uint64 uid_instance; // Instance ID, computed once.
uint64 calculate_unique_id()
{
if ( uid_instance == 0 )
{
// This is the first time we need a UID.
if ( ! have_random_seed() )
{
// If we don't need deterministic output (as
// indicated by a set seed), we calculate the
// instance ID by hashing something likely to be
// globally unique.
struct {
char hostname[128];
struct timeval time;
pid_t pid;
int rnd;
} unique;
gethostname(unique.hostname, 128);
unique.hostname[sizeof(unique.hostname)-1] = '\0';
gettimeofday(&unique.time, 0);
unique.pid = getpid();
unique.rnd = bro_random();
uid_instance = HashKey::HashBytes(&unique, sizeof(unique));
++uid_instance; // Now it's larger than zero.
}
else
// Generate determistic UIDs.
uid_instance = 1;
}
// Now calculate the unique ID.
struct {
uint64 counter;
hash_t instance;
} key;
key.counter = ++uid_counter;
key.instance = uid_instance;
uint64_t h = HashKey::HashBytes(&key, sizeof(key));
return h;
}
void out_of_memory(const char* where)
{
bro_logger->FatalError("out of memory in %s.\n", where);
}
void get_memory_usage(unsigned int* total, unsigned int* malloced)
{
unsigned int ret_total;
#ifdef HAVE_MALLINFO
// For memory, getrusage() gives bogus results on Linux. Grmpf.
struct mallinfo mi = mallinfo();
if ( malloced )
*malloced = mi.uordblks;
ret_total = mi.arena;
if ( total )
*total = ret_total;
#else
struct rusage r;
getrusage(RUSAGE_SELF, &r);
if ( malloced )
*malloced = 0;
// At least on FreeBSD it's in KB.
ret_total = r.ru_maxrss * 1024;
if ( total )
*total = ret_total;
#endif
// return ret_total;
}
#ifdef malloc
#undef malloc
#undef realloc
#undef free
extern "C" {
void* malloc(size_t);
void* realloc(void*, size_t);
void free(void*);
}
static int malloc_debug = 0;
void* debug_malloc(size_t t)
{
void* v = malloc(t);
if ( malloc_debug )
printf("%.6f malloc %x %d\n", network_time, v, t);
return v;
}
void* debug_realloc(void* v, size_t t)
{
v = realloc(v, t);
if ( malloc_debug )
printf("%.6f realloc %x %d\n", network_time, v, t);
return v;
}
void debug_free(void* v)
{
if ( malloc_debug )
printf("%.6f free %x\n", network_time, v);
free(v);
}
void* operator new(size_t t)
{
void* v = malloc(t);
if ( malloc_debug )
printf("%.6f new %x %d\n", network_time, v, t);
return v;
}
void* operator new[](size_t t)
{
void* v = malloc(t);
if ( malloc_debug )
printf("%.6f new[] %x %d\n", network_time, v, t);
return v;
}
void operator delete(void* v)
{
if ( malloc_debug )
printf("%.6f delete %x\n", network_time, v);
free(v);
}
void operator delete[](void* v)
{
if ( malloc_debug )
printf("%.6f delete %x\n", network_time, v);
free(v);
}
#endif