zeek/src/File.cc
Benjamin Bannier f5a76c1aed Reformat Zeek in Spicy style
This largely copies over Spicy's `.clang-format` configuration file. The
one place where we deviate is header include order since Zeek depends on
headers being included in a certain order.
2023-10-30 09:40:55 +01:00

326 lines
6.4 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/File.h"
#include "zeek/zeek-config.h"
#include <sys/types.h>
#ifdef TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <ctime>
#else
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <ctime>
#endif
#endif
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include "zeek/Attr.h"
#include "zeek/Desc.h"
#include "zeek/Event.h"
#include "zeek/Expr.h"
#include "zeek/NetVar.h"
#include "zeek/Reporter.h"
#include "zeek/RunState.h"
#include "zeek/Type.h"
#include "zeek/Var.h"
namespace zeek {
std::list<std::pair<std::string, File*>> File::open_files;
// Maximizes the number of open file descriptors.
static void maximize_num_fds() {
struct rlimit rl;
if ( getrlimit(RLIMIT_NOFILE, &rl) < 0 )
reporter->FatalError("maximize_num_fds(): getrlimit failed");
if ( rl.rlim_max == RLIM_INFINITY ) {
// Don't try raising the current limit.
return;
}
// See if we can raise the current to the maximum.
rl.rlim_cur = rl.rlim_max;
if ( setrlimit(RLIMIT_NOFILE, &rl) < 0 )
reporter->FatalError("maximize_num_fds(): setrlimit failed");
}
File::File(FILE* arg_f) {
Init();
f = arg_f;
name = access = nullptr;
t = base_type(TYPE_STRING);
is_open = (f != nullptr);
}
File::File(FILE* arg_f, const char* arg_name, const char* arg_access) {
Init();
f = arg_f;
name = util::copy_string(arg_name);
access = util::copy_string(arg_access);
t = base_type(TYPE_STRING);
is_open = (f != nullptr);
}
File::File(const char* arg_name, const char* arg_access) {
Init();
f = nullptr;
name = util::copy_string(arg_name);
access = util::copy_string(arg_access);
t = base_type(TYPE_STRING);
if ( util::streq(name, "/dev/stdin") )
f = stdin;
else if ( util::streq(name, "/dev/stdout") )
f = stdout;
else if ( util::streq(name, "/dev/stderr") )
f = stderr;
if ( f )
is_open = true;
else if ( ! Open() ) {
reporter->Error("cannot open %s: %s", name, strerror(errno));
is_open = false;
}
}
const char* File::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 nullptr;
}
bool File::Open(FILE* file, const char* mode) {
static bool fds_maximized = false;
open_time = run_state::network_time ? run_state::network_time : util::current_time();
if ( ! fds_maximized ) {
// Haven't initialized yet.
maximize_num_fds();
fds_maximized = true;
}
f = file;
if ( ! f ) {
if ( ! mode )
f = fopen(name, access);
else
f = fopen(name, mode);
}
SetBuf(buffered);
if ( ! f ) {
is_open = false;
return false;
}
is_open = true;
open_files.emplace_back(name, this);
RaiseOpenEvent();
return true;
}
File::~File() {
Close();
Unref(attrs);
delete[] name;
delete[] access;
#ifdef USE_PERFTOOLS_DEBUG
heap_checker->UnIgnoreObject(this);
#endif
}
void File::Init() {
open_time = 0;
is_open = false;
attrs = nullptr;
buffered = true;
raw_output = false;
#ifdef USE_PERFTOOLS_DEBUG
heap_checker->IgnoreObject(this);
#endif
}
FILE* File::FileHandle() { return f; }
FILE* File::Seek(long new_position) {
if ( ! FileHandle() )
return nullptr;
if ( fseek(f, new_position, SEEK_SET) < 0 )
reporter->Error("seek failed");
return f;
}
void File::SetBuf(bool arg_buffered) {
if ( ! f )
return;
if ( util::detail::setvbuf(f, NULL, arg_buffered ? _IOFBF : _IOLBF, 0) != 0 )
reporter->Error("setvbuf failed");
buffered = arg_buffered;
}
bool File::Close() {
if ( ! is_open )
return true;
// Do not close stdin/stdout/stderr.
if ( f == stdin || f == stdout || f == stderr )
return false;
if ( ! f )
return false;
fclose(f);
f = nullptr;
open_time = 0;
is_open = false;
Unlink();
return true;
}
void File::Unlink() {
for ( auto it = open_files.begin(); it != open_files.end(); ++it ) {
if ( (*it).second == this ) {
open_files.erase(it);
return;
}
}
}
void File::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 File::SetAttrs(detail::Attributes* arg_attrs) {
if ( ! arg_attrs )
return;
attrs = arg_attrs;
Ref(attrs);
if ( attrs->Find(detail::ATTR_RAW_OUTPUT) )
EnableRawOutput();
}
RecordVal* File::Rotate() {
if ( ! is_open )
return nullptr;
// Do not rotate stdin/stdout/stderr.
if ( f == stdin || f == stdout || f == stderr )
return nullptr;
static auto rotate_info = id::find_type<RecordType>("rotate_info");
auto* info = new RecordVal(rotate_info);
FILE* newf = util::detail::rotate_file(name, info);
if ( ! newf ) {
Unref(info);
return nullptr;
}
info->AssignTime(2, open_time);
Unlink();
fclose(f);
f = nullptr;
Open(newf);
return info;
}
void File::CloseOpenFiles() {
auto it = open_files.begin();
while ( it != open_files.end() ) {
auto el = it++;
(*el).second->Close();
}
}
bool File::Write(const char* data, int len) {
if ( ! is_open )
return false;
if ( ! len )
len = strlen(data);
if ( fwrite(data, len, 1, f) < 1 )
return false;
return true;
}
void File::RaiseOpenEvent() {
if ( ! ::file_opened )
return;
FilePtr bf{NewRef{}, this};
auto* event = new Event(::file_opened, {make_intrusive<FileVal>(std::move(bf))});
event_mgr.Dispatch(event, true);
}
double File::Size() {
fflush(f);
struct stat s;
if ( fstat(fileno(f), &s) < 0 ) {
reporter->Error("can't stat fd for %s: %s", name, strerror(errno));
return 0;
}
return s.st_size;
}
FilePtr File::Get(const char* name) {
for ( const auto& el : open_files )
if ( el.first == name )
return {NewRef{}, el.second};
return make_intrusive<File>(name, "w");
}
} // namespace zeek