mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
960 lines
18 KiB
C++
960 lines
18 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "config.h"
|
|
|
|
#include <sys/types.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 <sys/resource.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "File.h"
|
|
#include "Type.h"
|
|
#include "Timer.h"
|
|
#include "Expr.h"
|
|
#include "NetVar.h"
|
|
#include "Net.h"
|
|
#include "Serializer.h"
|
|
#include "Event.h"
|
|
#include "Reporter.h"
|
|
|
|
// Timer which on dispatching rotates the file.
|
|
class RotateTimer : public Timer {
|
|
public:
|
|
RotateTimer(double t, BroFile* f, bool arg_raise) : Timer(t, TIMER_ROTATE)
|
|
{ file = f; raise = arg_raise; name = copy_string(f->Name()); }
|
|
~RotateTimer();
|
|
|
|
void Dispatch(double t, int is_expire);
|
|
|
|
protected:
|
|
BroFile* file;
|
|
bool raise;
|
|
const char* name;
|
|
};
|
|
|
|
RotateTimer::~RotateTimer()
|
|
{
|
|
if ( file->rotate_timer == this )
|
|
file->rotate_timer = 0;
|
|
|
|
delete [] name;
|
|
}
|
|
|
|
void RotateTimer::Dispatch(double t, int is_expire)
|
|
{
|
|
file->rotate_timer = 0;
|
|
|
|
if ( ! is_expire )
|
|
{
|
|
if ( raise )
|
|
{
|
|
val_list* vl = new val_list;
|
|
Ref(file);
|
|
vl->append(new Val(file));
|
|
mgr.QueueEvent(rotate_interval, vl);
|
|
}
|
|
|
|
file->InstallRotateTimer();
|
|
}
|
|
}
|
|
|
|
|
|
// The following could in principle be part of a "file manager" object.
|
|
|
|
#define MAX_FILE_CACHE_SIZE 32
|
|
static int num_files_in_cache = 0;
|
|
static int max_files_in_cache = 0;
|
|
static BroFile* head = 0;
|
|
static BroFile* tail = 0;
|
|
|
|
double BroFile::default_rotation_interval = 0;
|
|
double BroFile::default_rotation_size = 0;
|
|
|
|
// Maximizes the number of open file descriptors and returns the number
|
|
// that we should use for the cache.
|
|
static int maximize_num_fds()
|
|
{
|
|
#ifdef NO_HAVE_SETRLIMIT
|
|
return MAX_FILE_CACHE_SIZE;
|
|
#else
|
|
struct rlimit rl;
|
|
if ( getrlimit(RLIMIT_NOFILE, &rl) < 0 )
|
|
reporter->InternalError("maximize_num_fds(): getrlimit failed");
|
|
|
|
if ( rl.rlim_max == RLIM_INFINITY )
|
|
{
|
|
// Don't try raising the current limit.
|
|
if ( rl.rlim_cur == RLIM_INFINITY )
|
|
// Let's not be too ambitious.
|
|
return MAX_FILE_CACHE_SIZE;
|
|
else
|
|
return rl.rlim_cur / 2;
|
|
}
|
|
|
|
// See if we can raise the current to the maximum.
|
|
rl.rlim_cur = rl.rlim_max;
|
|
|
|
if ( setrlimit(RLIMIT_NOFILE, &rl) < 0 )
|
|
reporter->InternalError("maximize_num_fds(): setrlimit failed");
|
|
|
|
return rl.rlim_cur / 2;
|
|
#endif
|
|
}
|
|
|
|
|
|
BroFile::BroFile(FILE* arg_f)
|
|
{
|
|
Init();
|
|
f = arg_f;
|
|
name = access = 0;
|
|
t = base_type(TYPE_STRING);
|
|
is_open = (f != 0);
|
|
|
|
if ( f )
|
|
UpdateFileSize();
|
|
}
|
|
|
|
BroFile::BroFile(FILE* arg_f, const char* arg_name, const char* arg_access)
|
|
{
|
|
Init();
|
|
f = arg_f;
|
|
name = copy_string(arg_name);
|
|
access = copy_string(arg_access);
|
|
t = base_type(TYPE_STRING);
|
|
is_open = (f != 0);
|
|
|
|
if ( f )
|
|
UpdateFileSize();
|
|
}
|
|
|
|
BroFile::BroFile(const char* arg_name, const char* arg_access, BroType* arg_t)
|
|
{
|
|
Init();
|
|
|
|
name = copy_string(arg_name);
|
|
access = copy_string(arg_access);
|
|
t = arg_t ? arg_t : base_type(TYPE_STRING);
|
|
if ( ! Open() )
|
|
{
|
|
reporter->Error(fmt("cannot open %s: %s", name, strerror(errno)));
|
|
is_open = 0;
|
|
okay_to_manage = 0;
|
|
}
|
|
}
|
|
|
|
const char* BroFile::Name() const
|
|
{
|
|
if ( name )
|
|
return name;
|
|
|
|
if ( f == stdin )
|
|
return"/dev/stdin";
|
|
|
|
if ( f == stdout )
|
|
return "/dev/stdout";
|
|
|
|
if ( f == stderr )
|
|
return "/dev/stderr";
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool BroFile::Open(FILE* file)
|
|
{
|
|
open_time = network_time ? network_time : current_time();
|
|
|
|
if ( ! max_files_in_cache )
|
|
// Haven't initialized yet.
|
|
max_files_in_cache = maximize_num_fds();
|
|
|
|
if ( num_files_in_cache >= max_files_in_cache )
|
|
PurgeCache();
|
|
|
|
f = file;
|
|
|
|
if ( default_rotation_interval &&
|
|
(! attrs || ! attrs->FindAttr(ATTR_ROTATE_INTERVAL)) )
|
|
rotate_interval = default_rotation_interval;
|
|
|
|
if ( default_rotation_size &&
|
|
(! attrs || ! attrs->FindAttr(ATTR_ROTATE_SIZE)) )
|
|
rotate_size = default_rotation_size;
|
|
|
|
InstallRotateTimer();
|
|
|
|
if ( ! f )
|
|
f = fopen(name, access);
|
|
|
|
SetBuf(buffered);
|
|
|
|
if ( f )
|
|
{
|
|
// These are the only files we manage, because we open them
|
|
// ourselves and hence don't have any surprises regarding
|
|
// whether we're allowed to close them.
|
|
is_open = okay_to_manage = 1;
|
|
|
|
InsertAtBeginning();
|
|
UpdateFileSize();
|
|
}
|
|
else
|
|
{
|
|
// No point managing it.
|
|
is_open = okay_to_manage = 0;
|
|
return false;
|
|
}
|
|
|
|
RaiseOpenEvent();
|
|
|
|
return true;
|
|
}
|
|
|
|
BroFile::~BroFile()
|
|
{
|
|
Close();
|
|
Unref(t);
|
|
Unref(attrs);
|
|
|
|
delete [] name;
|
|
delete [] access;
|
|
delete [] cipher_buffer;
|
|
|
|
#ifdef USE_PERFTOOLS
|
|
heap_checker->UnIgnoreObject(this);
|
|
#endif
|
|
}
|
|
|
|
void BroFile::Init()
|
|
{
|
|
is_open = okay_to_manage = is_in_cache = 0;
|
|
position = 0;
|
|
next = prev = 0;
|
|
rotate_timer = 0;
|
|
rotate_interval = 0.0;
|
|
rotate_size = current_size = 0.0;
|
|
open_time = 0;
|
|
attrs = 0;
|
|
buffered = true;
|
|
print_hook = true;
|
|
raw_output = false;
|
|
t = 0;
|
|
pub_key = 0;
|
|
cipher_ctx = 0;
|
|
cipher_buffer = 0;
|
|
|
|
#ifdef USE_PERFTOOLS
|
|
heap_checker->IgnoreObject(this);
|
|
#endif
|
|
}
|
|
|
|
FILE* BroFile::File()
|
|
{
|
|
if ( okay_to_manage && ! is_in_cache )
|
|
f = BringIntoCache();
|
|
|
|
return f;
|
|
}
|
|
|
|
FILE* BroFile::BringIntoCache()
|
|
{
|
|
if ( f )
|
|
reporter->InternalError("BroFile non-nil non-open file");
|
|
|
|
if ( num_files_in_cache >= max_files_in_cache )
|
|
PurgeCache();
|
|
|
|
if ( position == 0 )
|
|
// Need to truncate it.
|
|
f = fopen(name, access);
|
|
else
|
|
// Don't clobber it.
|
|
f = fopen(name, "a");
|
|
|
|
if ( ! f )
|
|
{
|
|
reporter->Error("can't open %s", name);
|
|
|
|
f = fopen("/dev/null", "w");
|
|
|
|
if ( ! f )
|
|
reporter->InternalError("out of file descriptors");
|
|
|
|
okay_to_manage = 0;
|
|
return f;
|
|
}
|
|
|
|
RaiseOpenEvent();
|
|
UpdateFileSize();
|
|
|
|
if ( fseek(f, position, SEEK_SET) < 0 )
|
|
reporter->Error("reopen seek failed");
|
|
|
|
InsertAtBeginning();
|
|
|
|
return f;
|
|
}
|
|
|
|
FILE* BroFile::Seek(long new_position)
|
|
{
|
|
if ( ! File() )
|
|
return 0;
|
|
|
|
if ( fseek(f, new_position, SEEK_SET) < 0 )
|
|
reporter->Error("seek failed");
|
|
|
|
return f;
|
|
}
|
|
|
|
void BroFile::SetBuf(bool arg_buffered)
|
|
{
|
|
if ( ! f )
|
|
return;
|
|
|
|
if ( setvbuf(f, NULL, arg_buffered ? _IOFBF : _IOLBF, 0) != 0 )
|
|
reporter->Error("setvbuf failed");
|
|
|
|
buffered = arg_buffered;
|
|
}
|
|
|
|
int BroFile::Close()
|
|
{
|
|
if ( rotate_timer )
|
|
{
|
|
timer_mgr->Cancel(rotate_timer);
|
|
rotate_timer = 0;
|
|
}
|
|
|
|
if ( ! is_open )
|
|
return 1;
|
|
|
|
FinishEncrypt();
|
|
|
|
// Do not close stdout/stderr.
|
|
if ( f == stdout || f == stderr )
|
|
return 0;
|
|
|
|
if ( is_in_cache )
|
|
{
|
|
Unlink();
|
|
if ( f )
|
|
{
|
|
fclose(f);
|
|
f = 0;
|
|
open_time = 0;
|
|
}
|
|
|
|
is_open = 0;
|
|
okay_to_manage = 0; // no longer managed since will never reopen
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Not managed.
|
|
if ( ! f )
|
|
return 0;
|
|
|
|
fclose(f);
|
|
f = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void BroFile::Suspend()
|
|
{
|
|
if ( ! is_in_cache )
|
|
reporter->InternalError("BroFile::Suspend() called for non-cached file");
|
|
if ( ! is_open )
|
|
reporter->InternalError("BroFile::Suspend() called for non-open file");
|
|
|
|
Unlink();
|
|
|
|
if ( ! f )
|
|
reporter->InternalError("BroFile::Suspend() called for nil file");
|
|
|
|
if ( (position = ftell(f)) < 0 )
|
|
{
|
|
reporter->Error("ftell failed");
|
|
position = 0;
|
|
}
|
|
|
|
fclose(f);
|
|
f = 0;
|
|
}
|
|
|
|
void BroFile::PurgeCache()
|
|
{
|
|
if ( ! tail )
|
|
reporter->InternalError("BroFile purge of empty cache");
|
|
|
|
tail->Suspend();
|
|
}
|
|
|
|
void BroFile::Unlink()
|
|
{
|
|
if ( is_in_cache )
|
|
{
|
|
if ( head == this )
|
|
head = Next();
|
|
else
|
|
Prev()->SetNext(next);
|
|
|
|
if ( tail == this )
|
|
tail = Prev();
|
|
else
|
|
Next()->SetPrev(prev);
|
|
|
|
if ( (head || tail) && ! (head && tail) )
|
|
reporter->InternalError("BroFile link list botch");
|
|
|
|
is_in_cache = 0;
|
|
prev = next = 0;
|
|
|
|
if ( --num_files_in_cache < 0 )
|
|
reporter->InternalError("BroFile underflow of file cache");
|
|
}
|
|
}
|
|
|
|
void BroFile::InsertAtBeginning()
|
|
{
|
|
if ( ! head )
|
|
{
|
|
head = tail = this;
|
|
next = prev = 0;
|
|
}
|
|
else
|
|
{
|
|
SetNext(head);
|
|
SetPrev(0);
|
|
head->SetPrev(this);
|
|
head = this;
|
|
}
|
|
|
|
if ( ++num_files_in_cache > max_files_in_cache )
|
|
reporter->InternalError("BroFile overflow of file cache");
|
|
|
|
is_in_cache = 1;
|
|
}
|
|
|
|
void BroFile::MoveToBeginning()
|
|
{
|
|
if ( head == this )
|
|
return; // already at the beginning
|
|
|
|
if ( ! is_in_cache || ! prev )
|
|
reporter->InternalError("BroFile inconsistency in MoveToBeginning()");
|
|
|
|
Unlink();
|
|
InsertAtBeginning();
|
|
}
|
|
|
|
void BroFile::Describe(ODesc* d) const
|
|
{
|
|
d->AddSP("file");
|
|
|
|
if ( name )
|
|
{
|
|
d->Add("\"");
|
|
d->Add(name);
|
|
d->AddSP("\"");
|
|
}
|
|
|
|
d->AddSP("of");
|
|
if ( t )
|
|
t->Describe(d);
|
|
else
|
|
d->Add("(no type)");
|
|
}
|
|
|
|
void BroFile::SetAttrs(Attributes* arg_attrs)
|
|
{
|
|
if ( ! arg_attrs )
|
|
return;
|
|
|
|
attrs = arg_attrs;
|
|
Ref(attrs);
|
|
|
|
Attr* ef = attrs->FindAttr(ATTR_ROTATE_INTERVAL);
|
|
if ( ef )
|
|
rotate_interval = ef->AttrExpr()->ExprVal()->AsInterval();
|
|
|
|
ef = attrs->FindAttr(ATTR_ROTATE_SIZE);
|
|
if ( ef )
|
|
rotate_size = ef->AttrExpr()->ExprVal()->AsDouble();
|
|
|
|
ef = attrs->FindAttr(ATTR_ENCRYPT);
|
|
if ( ef )
|
|
{
|
|
if ( ef->AttrExpr() )
|
|
InitEncrypt(ef->AttrExpr()->ExprVal()->AsString()->CheckString());
|
|
else
|
|
InitEncrypt(log_encryption_key->AsString()->CheckString());
|
|
}
|
|
|
|
if ( attrs->FindAttr(ATTR_DISABLE_PRINT_HOOK) )
|
|
DisablePrintHook();
|
|
|
|
if ( attrs->FindAttr(ATTR_RAW_OUTPUT) )
|
|
EnableRawOutput();
|
|
|
|
InstallRotateTimer();
|
|
}
|
|
|
|
void BroFile::SetRotateInterval(double secs)
|
|
{
|
|
rotate_interval = secs;
|
|
InstallRotateTimer();
|
|
}
|
|
|
|
RecordVal* BroFile::Rotate()
|
|
{
|
|
if ( ! is_open )
|
|
return 0;
|
|
|
|
if ( okay_to_manage && ! is_in_cache )
|
|
BringIntoCache();
|
|
|
|
RecordVal* info = new RecordVal(rotate_info);
|
|
FILE* newf = rotate_file(name, info);
|
|
|
|
if ( ! newf )
|
|
{
|
|
Unref(info);
|
|
return 0;
|
|
}
|
|
|
|
info->Assign(2, new Val(open_time, TYPE_TIME));
|
|
|
|
Unlink();
|
|
fclose(f);
|
|
f = 0;
|
|
|
|
Open(newf);
|
|
return info;
|
|
}
|
|
|
|
void BroFile::InstallRotateTimer()
|
|
{
|
|
if ( terminating )
|
|
return;
|
|
|
|
if ( rotate_timer )
|
|
{
|
|
timer_mgr->Cancel(rotate_timer);
|
|
rotate_timer = 0;
|
|
}
|
|
|
|
if ( rotate_interval )
|
|
{
|
|
// When this is called for the first time, network_time can
|
|
// still be zero. If so, we set a timer which fires
|
|
// immediately but doesn't rotate when it expires.
|
|
|
|
if ( ! network_time )
|
|
rotate_timer = new RotateTimer(1, this, false);
|
|
else
|
|
{
|
|
if ( ! open_time )
|
|
open_time = network_time;
|
|
|
|
const char* base_time = log_rotate_base_time ?
|
|
log_rotate_base_time->AsString()->CheckString() : 0;
|
|
|
|
double delta_t =
|
|
calc_next_rotate(rotate_interval, base_time);
|
|
rotate_timer = new RotateTimer(network_time + delta_t,
|
|
this, true);
|
|
}
|
|
|
|
timer_mgr->Add(rotate_timer);
|
|
}
|
|
}
|
|
|
|
void BroFile::SetDefaultRotation(double interval, double max_size)
|
|
{
|
|
for ( BroFile* f = head; f; f = f->next )
|
|
{
|
|
if ( ! (f->attrs && f->attrs->FindAttr(ATTR_ROTATE_INTERVAL)) )
|
|
{
|
|
f->rotate_interval = interval;
|
|
f->InstallRotateTimer();
|
|
}
|
|
|
|
if ( ! (f->attrs && f->attrs->FindAttr(ATTR_ROTATE_SIZE)) )
|
|
f->rotate_size = max_size;
|
|
}
|
|
|
|
default_rotation_interval = interval;
|
|
default_rotation_size = max_size;
|
|
}
|
|
|
|
void BroFile::CloseCachedFiles()
|
|
{
|
|
BroFile* next;
|
|
for ( BroFile* f = head; f; f = next )
|
|
{
|
|
// Send final rotate events (immediately).
|
|
if ( f->rotate_interval )
|
|
{
|
|
val_list* vl = new val_list;
|
|
Ref(f);
|
|
vl->append(new Val(f));
|
|
Event* event = new Event(::rotate_interval, vl);
|
|
mgr.Dispatch(event, true);
|
|
}
|
|
|
|
if ( f->rotate_size )
|
|
{
|
|
val_list* vl = new val_list;
|
|
Ref(f);
|
|
vl->append(new Val(f));
|
|
Event* event = new ::Event(::rotate_size, vl);
|
|
mgr.Dispatch(event, true);
|
|
}
|
|
|
|
next = f->next;
|
|
if ( f->is_in_cache )
|
|
f->Close();
|
|
}
|
|
}
|
|
|
|
void BroFile::InitEncrypt(const char* keyfile)
|
|
{
|
|
if ( ! (pub_key || keyfile) )
|
|
return;
|
|
|
|
if ( ! pub_key )
|
|
{
|
|
FILE* key = fopen(keyfile, "r");
|
|
|
|
if ( ! key )
|
|
{
|
|
reporter->Error(fmt("can't open key file %s: %s", keyfile, strerror(errno)));
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
pub_key = PEM_read_PUBKEY(key, 0, 0, 0);
|
|
if ( ! pub_key )
|
|
{
|
|
reporter->Error(fmt("can't read key from %s: %s", keyfile,
|
|
ERR_error_string(ERR_get_error(), 0)));
|
|
Close();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Depending on the OpenSSL version, EVP_*_cbc()
|
|
// returns a const or a non-const.
|
|
EVP_CIPHER* cipher_type = (EVP_CIPHER*) EVP_bf_cbc();
|
|
cipher_ctx = new EVP_CIPHER_CTX;
|
|
|
|
unsigned char secret[EVP_PKEY_size(pub_key)];
|
|
unsigned char* psecret = secret;
|
|
unsigned int secret_len;
|
|
|
|
int iv_len = EVP_CIPHER_iv_length(cipher_type);
|
|
unsigned char iv[iv_len];
|
|
|
|
if ( ! EVP_SealInit(cipher_ctx, cipher_type, &psecret,
|
|
(int*) &secret_len, iv, &pub_key, 1) )
|
|
{
|
|
reporter->Error(fmt("can't init cipher context for %s: %s", keyfile,
|
|
ERR_error_string(ERR_get_error(), 0)));
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
secret_len = htonl(secret_len);
|
|
|
|
if ( ! (fwrite("BROENC1", 7, 1, f) &&
|
|
fwrite(&secret_len, sizeof(secret_len), 1, f) &&
|
|
fwrite(secret, ntohl(secret_len), 1, f) &&
|
|
fwrite(iv, iv_len, 1, f)) )
|
|
{
|
|
reporter->Error(fmt("can't write header to log file %s: %s",
|
|
name, strerror(errno)));
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
int buf_size = MIN_BUFFER_SIZE + EVP_CIPHER_block_size(cipher_type);
|
|
cipher_buffer = new unsigned char[buf_size];
|
|
}
|
|
|
|
void BroFile::FinishEncrypt()
|
|
{
|
|
if ( ! is_open )
|
|
return;
|
|
|
|
if ( ! pub_key )
|
|
return;
|
|
|
|
if ( cipher_ctx )
|
|
{
|
|
int outl;
|
|
EVP_SealFinal(cipher_ctx, cipher_buffer, &outl);
|
|
|
|
if ( outl && ! fwrite(cipher_buffer, outl, 1, f) )
|
|
{
|
|
reporter->Error(fmt("write error for %s: %s",
|
|
name, strerror(errno)));
|
|
return;
|
|
}
|
|
|
|
delete cipher_ctx;
|
|
cipher_ctx = 0;
|
|
}
|
|
}
|
|
|
|
|
|
int BroFile::Write(const char* data, int len)
|
|
{
|
|
if ( ! is_open )
|
|
return 0;
|
|
|
|
if ( ! is_in_cache && okay_to_manage )
|
|
BringIntoCache();
|
|
|
|
if ( ! len )
|
|
len = strlen(data);
|
|
|
|
if ( cipher_ctx )
|
|
{
|
|
while ( len )
|
|
{
|
|
int outl;
|
|
int inl = min(+MIN_BUFFER_SIZE, len);
|
|
|
|
if ( ! EVP_SealUpdate(cipher_ctx, cipher_buffer, &outl,
|
|
(unsigned char*)data, inl) )
|
|
{
|
|
reporter->Error(fmt("encryption error for %s: %s",
|
|
name,
|
|
ERR_error_string(ERR_get_error(), 0)));
|
|
Close();
|
|
return 0;
|
|
}
|
|
|
|
if ( outl && ! fwrite(cipher_buffer, outl, 1, f) )
|
|
{
|
|
reporter->Error(fmt("write error for %s: %s",
|
|
name, strerror(errno)));
|
|
Close();
|
|
return 0;
|
|
}
|
|
|
|
data += inl;
|
|
len -= inl;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
len = fwrite(data, 1, len, f);
|
|
if ( len <= 0 )
|
|
return false;
|
|
|
|
if ( rotate_size && current_size < rotate_size && current_size + len >= rotate_size )
|
|
{
|
|
val_list* vl = new val_list;
|
|
vl->append(new Val(this));
|
|
mgr.QueueEvent(::rotate_size, vl);
|
|
}
|
|
|
|
// This does not work if we seek around. But none of the logs does that
|
|
// and we avoid stat()'ing the file all the time.
|
|
current_size += len;
|
|
|
|
return true;
|
|
}
|
|
|
|
void BroFile::RaiseOpenEvent()
|
|
{
|
|
if ( ! ::file_opened )
|
|
return;
|
|
|
|
val_list* vl = new val_list;
|
|
Ref(this);
|
|
vl->append(new Val(this));
|
|
Event* event = new ::Event(::file_opened, vl);
|
|
mgr.Dispatch(event, true);
|
|
}
|
|
|
|
void BroFile::UpdateFileSize()
|
|
{
|
|
struct stat s;
|
|
if ( fstat(fileno(f), &s) < 0 )
|
|
{
|
|
reporter->Error(fmt("can't stat fd for %s: %s", name, strerror(errno)));
|
|
current_size = 0;
|
|
return;
|
|
}
|
|
|
|
current_size = double(s.st_size);
|
|
}
|
|
|
|
bool BroFile::Serialize(SerialInfo* info) const
|
|
{
|
|
return SerialObj::Serialize(info);
|
|
}
|
|
|
|
BroFile* BroFile::GetFile(const char* name)
|
|
{
|
|
for ( BroFile* f = head; f; f = f->next )
|
|
{
|
|
if ( f->name && streq(name, f->name) )
|
|
return f;
|
|
}
|
|
|
|
return new BroFile(name, "w", 0);
|
|
}
|
|
|
|
BroFile* BroFile::Unserialize(UnserialInfo* info)
|
|
{
|
|
BroFile* file = (BroFile*) SerialObj::Unserialize(info, SER_BRO_FILE);
|
|
|
|
if ( ! file )
|
|
return 0;
|
|
|
|
if ( file->is_open )
|
|
return file;
|
|
|
|
// If there is already an object for this file, return it.
|
|
if ( file->name )
|
|
{
|
|
for ( BroFile* f = head; f; f = f->next )
|
|
{
|
|
if ( f->name && streq(file->name, f->name) )
|
|
{
|
|
Unref(file);
|
|
Ref(f);
|
|
return f;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, open.
|
|
if ( ! file->Open() )
|
|
{
|
|
info->s->Error(fmt("cannot open %s: %s",
|
|
file->name, strerror(errno)));
|
|
return 0;
|
|
}
|
|
|
|
// Here comes a hack. This method will return a pointer to a newly
|
|
// instantiated file object. As soon as this pointer is Unref'ed, the
|
|
// file will be closed. That means that when we unserialize the same
|
|
// file next time, we will re-open it and thereby delete the first one,
|
|
// i.e., we will be keeping to delete what we've written just before.
|
|
//
|
|
// To avoid this loop, we do an extra Ref here, i.e., this file will
|
|
// *never* be closed anymore (as long the file cache does not overflow).
|
|
Ref(file);
|
|
|
|
// We deliberately override log rotation attributes with our defaults.
|
|
file->rotate_interval = log_rotate_interval;
|
|
file->rotate_size = log_max_size;
|
|
file->InstallRotateTimer();
|
|
file->SetBuf(file->buffered);
|
|
|
|
return file;
|
|
}
|
|
|
|
IMPLEMENT_SERIAL(BroFile, SER_BRO_FILE);
|
|
|
|
bool BroFile::DoSerialize(SerialInfo* info) const
|
|
{
|
|
DO_SERIALIZE(SER_BRO_FILE, BroObj);
|
|
|
|
const char* s = name;
|
|
|
|
if ( ! okay_to_manage )
|
|
{
|
|
// We can handle stdin/stdout/stderr but no others.
|
|
if ( f == stdin )
|
|
s = "/dev/stdin";
|
|
else if ( f == stdout )
|
|
s = "/dev/stdout";
|
|
else if ( f == stderr )
|
|
s = "/dev/stderr";
|
|
else
|
|
{
|
|
// We don't manage the file, and therefore don't
|
|
// really know how to pass it on to the other side.
|
|
// However, in order to not abort communication
|
|
// when this happens, we still send the name if we
|
|
// have one; or if we don't, we create a special
|
|
// "dont-have-a-file" file to be created on the
|
|
// receiver side.
|
|
if ( ! s )
|
|
s = "unmanaged-bro-output-file.log";
|
|
}
|
|
}
|
|
|
|
if ( ! (SERIALIZE(s) && SERIALIZE(buffered)) )
|
|
return false;
|
|
|
|
SERIALIZE_OPTIONAL_STR(access);
|
|
|
|
if ( ! t->Serialize(info) )
|
|
return false;
|
|
|
|
SERIALIZE_OPTIONAL(attrs);
|
|
return true;
|
|
}
|
|
|
|
bool BroFile::DoUnserialize(UnserialInfo* info)
|
|
{
|
|
DO_UNSERIALIZE(BroObj);
|
|
|
|
if ( ! (UNSERIALIZE_STR(&name, 0) && UNSERIALIZE(&buffered)) )
|
|
return false;
|
|
|
|
UNSERIALIZE_OPTIONAL_STR(access);
|
|
|
|
t = BroType::Unserialize(info);
|
|
if ( ! t )
|
|
return false;
|
|
|
|
UNSERIALIZE_OPTIONAL(attrs, Attributes::Unserialize(info));
|
|
|
|
// Parse attributes.
|
|
SetAttrs(attrs);
|
|
// SetAttrs() has ref'ed attrs again.
|
|
Unref(attrs);
|
|
|
|
// Bind stdin/stdout/stderr.
|
|
FILE* file = 0;
|
|
is_open = false;
|
|
f = 0;
|
|
|
|
if ( streq(name, "/dev/stdin") )
|
|
file = stdin;
|
|
else if ( streq(name, "/dev/stdout") )
|
|
file = stdout;
|
|
else if ( streq(name, "/dev/stderr") )
|
|
file = stderr;
|
|
|
|
if ( file )
|
|
{
|
|
delete [] name;
|
|
name = 0;
|
|
f = file;
|
|
is_open = true;
|
|
}
|
|
|
|
return true;
|
|
}
|