Refactor misc. Supervisor code

E.g. mostly to start taking advantage of C++17 things like
std::optional.  Also IntrusivePtr.
This commit is contained in:
Jon Siwek 2019-11-05 19:29:43 -08:00
parent c43ffc14fe
commit 8aa77436f9
5 changed files with 116 additions and 121 deletions

View file

@ -148,7 +148,8 @@ void net_update_time(double new_network_time)
void net_init(const std::vector<std::string>& interfaces,
const std::vector<std::string>& pcap_input_files,
const std::string& pcap_output_file, bool do_watchdog)
const std::optional<std::string>& pcap_output_file,
bool do_watchdog)
{
if ( ! pcap_input_files.empty() )
{
@ -189,9 +190,9 @@ void net_init(const std::vector<std::string>& interfaces,
// a timer.
reading_traces = reading_live = 0;
if ( ! pcap_output_file.empty() )
if ( pcap_output_file )
{
const char* writefile = pcap_output_file.data();
const char* writefile = pcap_output_file->data();
pkt_dumper = iosource_mgr->OpenPktDumper(writefile, false);
assert(pkt_dumper);

View file

@ -4,6 +4,7 @@
#include <vector>
#include <string>
#include <optional>
#include "net_util.h"
#include "util.h"
@ -15,7 +16,8 @@
extern void net_init(const std::vector<std::string>& interfaces,
const std::vector<std::string>& pcap_input_files,
const std::string& pcap_output_file, bool do_watchdog);
const std::optional<std::string>& pcap_output_file,
bool do_watchdog);
extern void net_run();
extern void net_get_final_stats();
extern void net_finish(int drain_events);

View file

@ -20,39 +20,41 @@ extern "C" {
#include "setsignal.h"
}
using namespace zeek;
namespace {
struct Stem {
Stem(std::unique_ptr<bro::PipePair> p);
~Stem();
zeek::Supervisor::Node* Run();
Supervisor::Node* Run();
zeek::Supervisor::Node* Poll();
Supervisor::Node* Poll();
zeek::Supervisor::Node* Revive();
Supervisor::Node* Revive();
void Reap();
bool Spawn(zeek::Supervisor::Node* node);
bool Spawn(Supervisor::Node* node);
int AliveNodeCount() const;
void KillNodes(int signal) const;
void KillNode(const zeek::Supervisor::Node& node, int signal) const;
void KillNode(const Supervisor::Node& node, int signal) const;
void Destroy(zeek::Supervisor::Node* node) const;
void Destroy(Supervisor::Node* node) const;
bool Wait(zeek::Supervisor::Node* node, int options) const;
bool Wait(Supervisor::Node* node, int options) const;
void Shutdown(int exit_code);
void ReportStatus(const zeek::Supervisor::Node& node) const;
void ReportStatus(const Supervisor::Node& node) const;
std::unique_ptr<bro::Flare> signal_flare;
std::unique_ptr<bro::PipePair> pipe;
std::map<std::string, zeek::Supervisor::Node> nodes;
std::map<std::string, Supervisor::Node> nodes;
std::string msg_buffer;
bool shutting_down = false;
};
@ -80,7 +82,7 @@ static RETSIGTYPE supervisor_sig_handler(int signo)
{
// TODO: signal safety
DBG_LOG(DBG_SUPERVISOR, "received signal: %d", signo);
zeek::supervisor->ObserveChildSignal();
supervisor->ObserveChildSignal();
return RETSIGVAL;
}
@ -104,13 +106,13 @@ static std::vector<std::string> extract_messages(std::string* buffer)
return rval;
}
static std::string make_create_message(const zeek::Supervisor::Node& node)
static std::string make_create_message(const Supervisor::Node& node)
{
auto json_str = node.ToJSON();
return fmt("create %s %s", node.name.data(), json_str.data());
}
zeek::Supervisor::Supervisor(zeek::Supervisor::Config cfg,
Supervisor::Supervisor(Supervisor::Config cfg,
std::unique_ptr<bro::PipePair> pipe,
pid_t arg_stem_pid)
: config(std::move(cfg)), stem_pid(arg_stem_pid), stem_pipe(std::move(pipe))
@ -121,7 +123,7 @@ zeek::Supervisor::Supervisor(zeek::Supervisor::Config cfg,
SetIdle(true);
}
zeek::Supervisor::~Supervisor()
Supervisor::~Supervisor()
{
setsignal(SIGCHLD, SIG_DFL);
@ -156,12 +158,12 @@ zeek::Supervisor::~Supervisor()
}
}
void zeek::Supervisor::ObserveChildSignal()
void Supervisor::ObserveChildSignal()
{
signal_flare.Fire();
}
void zeek::Supervisor::ReapStem()
void Supervisor::ReapStem()
{
if ( ! stem_pid )
return;
@ -199,7 +201,7 @@ void zeek::Supervisor::ReapStem()
" of stem process for unknown reason");
}
void zeek::Supervisor::HandleChildSignal()
void Supervisor::HandleChildSignal()
{
bool had_child_signal = signal_flare.Extinguish();
@ -270,19 +272,19 @@ void zeek::Supervisor::HandleChildSignal()
}
}
void zeek::Supervisor::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
void Supervisor::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
iosource::FD_Set* except)
{
read->Insert(signal_flare.FD());
read->Insert(stem_pipe->InFD());
}
double zeek::Supervisor::NextTimestamp(double* local_network_time)
double Supervisor::NextTimestamp(double* local_network_time)
{
return timer_mgr->Time();
}
void zeek::Supervisor::Process()
void Supervisor::Process()
{
HandleChildSignal();
@ -357,7 +359,7 @@ void Stem::Reap()
}
}
bool Stem::Wait(zeek::Supervisor::Node* node, int options) const
bool Stem::Wait(Supervisor::Node* node, int options) const
{
int status;
auto res = waitpid(node->pid, &status, options);
@ -394,7 +396,7 @@ bool Stem::Wait(zeek::Supervisor::Node* node, int options) const
return true;
}
void Stem::KillNode(const zeek::Supervisor::Node& node, int signal) const
void Stem::KillNode(const Supervisor::Node& node, int signal) const
{
auto kill_res = kill(node.pid, signal);
@ -403,7 +405,7 @@ void Stem::KillNode(const zeek::Supervisor::Node& node, int signal) const
node.name.data(), strerror(errno));
}
void Stem::Destroy(zeek::Supervisor::Node* node) const
void Stem::Destroy(Supervisor::Node* node) const
{
constexpr auto max_term_attempts = 13;
constexpr auto kill_delay = 2;
@ -424,7 +426,7 @@ void Stem::Destroy(zeek::Supervisor::Node* node) const
}
}
zeek::Supervisor::Node* Stem::Revive()
Supervisor::Node* Stem::Revive()
{
constexpr auto attempts_before_delay_increase = 3;
constexpr auto delay_increase_factor = 2;
@ -459,7 +461,7 @@ zeek::Supervisor::Node* Stem::Revive()
node.revival_delay *= delay_increase_factor;
if ( Spawn(&node) )
return new zeek::Supervisor::Node(node);
return new Supervisor::Node(node);
ReportStatus(node);
}
@ -467,7 +469,7 @@ zeek::Supervisor::Node* Stem::Revive()
return {};
}
bool Stem::Spawn(zeek::Supervisor::Node* node)
bool Stem::Spawn(Supervisor::Node* node)
{
auto node_pid = fork();
@ -552,13 +554,13 @@ void Stem::Shutdown(int exit_code)
}
}
void Stem::ReportStatus(const zeek::Supervisor::Node& node) const
void Stem::ReportStatus(const Supervisor::Node& node) const
{
std::string msg = fmt("status %s %d", node.name.data(), node.pid);
safe_write(pipe->OutFD(), msg.data(), msg.size() + 1);
}
zeek::Supervisor::Node* Stem::Run()
Supervisor::Node* Stem::Run()
{
for ( ; ; )
{
@ -571,7 +573,7 @@ zeek::Supervisor::Node* Stem::Run()
return {};
}
zeek::Supervisor::Node* Stem::Poll()
Supervisor::Node* Stem::Poll()
{
pollfd fds[2] = { { pipe->InFD(), POLLIN, 0 },
{ signal_flare->FD(), POLLIN, 0} };
@ -650,10 +652,10 @@ zeek::Supervisor::Node* Stem::Poll()
{
const auto& node_json = msg_tokens[2];
assert(nodes.find(node_name) == nodes.end());
auto node = zeek::Supervisor::Node::FromJSON(node_json);
auto node = Supervisor::Node::FromJSON(node_json);
if ( Spawn(&node) )
return new zeek::Supervisor::Node(node);
return new Supervisor::Node(node);
// TODO: get stem printfs going through standard Zeek debug.log
printf("Stem created node: %s (%d)\n", node.name.data(), node.pid);
@ -678,7 +680,7 @@ zeek::Supervisor::Node* Stem::Poll()
Destroy(&node);
if ( Spawn(&node) )
return new zeek::Supervisor::Node(node);
return new Supervisor::Node(node);
ReportStatus(node);
}
@ -689,7 +691,7 @@ zeek::Supervisor::Node* Stem::Poll()
return {};
}
zeek::Supervisor::Node* zeek::Supervisor::RunStem(std::unique_ptr<bro::PipePair> pipe)
Supervisor::Node* Supervisor::RunStem(std::unique_ptr<bro::PipePair> pipe)
{
Stem s(std::move(pipe));
return s.Run();
@ -709,9 +711,9 @@ static BifEnum::Supervisor::ClusterRole role_str_to_enum(const std::string& r)
return BifEnum::Supervisor::NONE;
}
zeek::Supervisor::Node zeek::Supervisor::Node::FromRecord(const RecordVal* node)
Supervisor::Node Supervisor::Node::FromRecord(const RecordVal* node)
{
zeek::Supervisor::Node rval;
Supervisor::Node rval;
rval.name = node->Lookup("name")->AsString()->CheckString();
auto iface_val = node->Lookup("interface");
@ -726,12 +728,11 @@ zeek::Supervisor::Node zeek::Supervisor::Node::FromRecord(const RecordVal* node)
while ( (v = cluster_table->NextEntry(k, c)) )
{
auto key = cluster_table_val->RecoverIndex(k);
IntrusivePtr<ListVal> key{cluster_table_val->RecoverIndex(k), false};
auto name = key->Index(0)->AsStringVal()->ToStdString();
Unref(key);
auto rv = v->Value()->AsRecordVal();
zeek::Supervisor::ClusterEndpoint ep;
Supervisor::ClusterEndpoint ep;
ep.role = static_cast<BifEnum::Supervisor::ClusterRole>(rv->Lookup("role")->AsEnum());
ep.host = rv->Lookup("host")->AsAddr().AsString();
ep.port = rv->Lookup("p")->AsPortVal()->Port();
@ -747,9 +748,9 @@ zeek::Supervisor::Node zeek::Supervisor::Node::FromRecord(const RecordVal* node)
return rval;
}
zeek::Supervisor::Node zeek::Supervisor::Node::FromJSON(const std::string& json)
Supervisor::Node Supervisor::Node::FromJSON(const std::string& json)
{
zeek::Supervisor::Node rval;
Supervisor::Node rval;
auto j = nlohmann::json::parse(json);
rval.name = j["name"];
@ -784,27 +785,23 @@ zeek::Supervisor::Node zeek::Supervisor::Node::FromJSON(const std::string& json)
return rval;
}
std::string zeek::Supervisor::Node::ToJSON() const
std::string Supervisor::Node::ToJSON() const
{
auto re = new RE_Matcher("^_");
auto re = std::make_unique<RE_Matcher>("^_");
auto node_val = ToRecord();
auto json_val = node_val->ToJSON(false, re);
IntrusivePtr<StringVal> json_val{node_val->ToJSON(false, re.get()), false};
auto rval = json_val->ToStdString();
delete re;
Unref(node_val);
Unref(json_val);
return rval;
}
RecordVal* zeek::Supervisor::Node::ToRecord() const
IntrusivePtr<RecordVal> Supervisor::Node::ToRecord() const
{
auto rt = BifType::Record::Supervisor::Node;
auto rval = new RecordVal(rt);
auto rval = make_intrusive<RecordVal>(rt);
rval->Assign(rt->FieldOffset("name"), new StringVal(name));
if ( ! interface.empty() )
rval->Assign(rt->FieldOffset("interface"),
new StringVal(interface));
if ( interface )
rval->Assign(rt->FieldOffset("interface"), new StringVal(*interface));
auto tt = BifType::Record::Supervisor::Node->FieldType("cluster");
auto cluster_val = new TableVal(tt->AsTableType());
@ -814,19 +811,18 @@ RecordVal* zeek::Supervisor::Node::ToRecord() const
{
auto& name = e.first;
auto& ep = e.second;
auto key = new StringVal(name);
auto key = make_intrusive<StringVal>(name);
auto ept = BifType::Record::Supervisor::ClusterEndpoint;
auto val = new RecordVal(ept);
auto val = make_intrusive<RecordVal>(ept);
val->Assign(ept->FieldOffset("role"), BifType::Enum::Supervisor::ClusterRole->GetVal(ep.role));
val->Assign(ept->FieldOffset("host"), new AddrVal(ep.host));
val->Assign(ept->FieldOffset("p"), val_mgr->GetPort(ep.port, TRANSPORT_TCP));
if ( ! ep.interface.empty() )
val->Assign(ept->FieldOffset("interface"), new StringVal(ep.interface));
if ( ep.interface )
val->Assign(ept->FieldOffset("interface"), new StringVal(*ep.interface));
cluster_val->Assign(key, val);
Unref(key);
cluster_val->Assign(key.get(), val.detach());
}
if ( pid )
@ -853,14 +849,14 @@ static Val* supervisor_role_to_cluster_node_type(BifEnum::Supervisor::ClusterRol
}
}
void zeek::Supervisor::Node::InitCluster()
void Supervisor::Node::InitCluster()
{
auto cluster_node_type = global_scope()->Lookup("Cluster::Node")->AsType()->AsRecordType();
auto cluster_nodes_id = global_scope()->Lookup("Cluster::nodes");
auto cluster_manager_is_logger_id = global_scope()->Lookup("Cluster::manager_is_logger");
auto cluster_nodes = cluster_nodes_id->ID_Val()->AsTableVal();
auto has_logger = false;
std::string manager_name;
std::optional<std::string> manager_name;
for ( const auto& e : supervised_node->cluster )
{
@ -874,30 +870,29 @@ void zeek::Supervisor::Node::InitCluster()
{
const auto& node_name = e.first;
const auto& ep = e.second;
auto key = new StringVal(node_name);
auto val = new RecordVal(cluster_node_type);
auto key = make_intrusive<StringVal>(node_name);
auto val = make_intrusive<RecordVal>(cluster_node_type);
auto node_type = supervisor_role_to_cluster_node_type(ep.role);
val->Assign(cluster_node_type->FieldOffset("node_type"), node_type);
val->Assign(cluster_node_type->FieldOffset("ip"), new AddrVal(ep.host));
val->Assign(cluster_node_type->FieldOffset("p"), val_mgr->GetPort(ep.port, TRANSPORT_TCP));
if ( ! ep.interface.empty() )
if ( ep.interface )
val->Assign(cluster_node_type->FieldOffset("interface"),
new StringVal(ep.interface));
new StringVal(*ep.interface));
if ( ! manager_name.empty() && ep.role != BifEnum::Supervisor::MANAGER )
if ( manager_name && ep.role != BifEnum::Supervisor::MANAGER )
val->Assign(cluster_node_type->FieldOffset("manager"),
new StringVal(manager_name));
new StringVal(*manager_name));
cluster_nodes->Assign(key, val);
Unref(key);
cluster_nodes->Assign(key.get(), val.detach());
}
cluster_manager_is_logger_id->SetVal(val_mgr->GetBool(! has_logger));
}
RecordVal* zeek::Supervisor::Status(const std::string& node_name)
RecordVal* Supervisor::Status(const std::string& node_name)
{
// TODO: handle node classes
auto rval = new RecordVal(BifType::Record::Supervisor::Status);
@ -908,22 +903,21 @@ RecordVal* zeek::Supervisor::Status(const std::string& node_name)
for ( const auto& n : nodes )
{
const auto& node = n.second;
auto key = new StringVal(node.name);
auto key = make_intrusive<StringVal>(node.name);
auto val = node.ToRecord();
node_table_val->Assign(key, val);
Unref(key);
node_table_val->Assign(key.get(), val.detach());
}
return rval;
}
std::string zeek::Supervisor::Create(const RecordVal* node_val)
std::string Supervisor::Create(const RecordVal* node_val)
{
auto node = zeek::Supervisor::Node::FromRecord(node_val);
auto node = Supervisor::Node::FromRecord(node_val);
return Create(node);
}
std::string zeek::Supervisor::Create(const zeek::Supervisor::Node& node)
std::string Supervisor::Create(const Supervisor::Node& node)
{
if ( node.name.find(' ') != std::string::npos )
return fmt("node names must not contain spaces: '%s'",
@ -938,7 +932,7 @@ std::string zeek::Supervisor::Create(const zeek::Supervisor::Node& node)
return "";
}
bool zeek::Supervisor::Destroy(const std::string& node_name)
bool Supervisor::Destroy(const std::string& node_name)
{
// TODO: handle node classes
@ -950,7 +944,7 @@ bool zeek::Supervisor::Destroy(const std::string& node_name)
return true;
}
bool zeek::Supervisor::Restart(const std::string& node_name)
bool Supervisor::Restart(const std::string& node_name)
{
// TODO: handle node classes

View file

@ -1,6 +1,7 @@
#pragma once
#include <sys/types.h>
#include <optional>
#include <cstdint>
#include <string>
#include <vector>
@ -13,6 +14,7 @@
#include "Pipe.h"
#include "Flare.h"
#include "NetVar.h"
#include "IntrusivePtr.h"
namespace zeek {
@ -29,7 +31,7 @@ public:
BifEnum::Supervisor::ClusterRole role;
std::string host;
int port;
std::string interface;
std::optional<std::string> interface;
};
struct Node {
@ -39,10 +41,10 @@ public:
static void InitCluster();
std::string ToJSON() const;
RecordVal* ToRecord() const;
IntrusivePtr<RecordVal> ToRecord() const;
std::string name;
std::string interface;
std::optional<std::string> interface;
std::map<std::string, ClusterEndpoint> cluster;
pid_t pid = 0;

View file

@ -9,6 +9,7 @@
#include <string.h>
#include <sys/types.h>
#include <list>
#include <optional>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
@ -219,11 +220,11 @@ struct zeek_options {
bool print_signature_debug_info = false;
int print_plugins = 0;
std::string debug_log_streams;
std::string debug_script_tracing_file;
std::optional<std::string> debug_log_streams;
std::optional<std::string> debug_script_tracing_file;
std::string identifier_to_print;
std::string script_code_to_exec;
std::optional<std::string> identifier_to_print;
std::optional<std::string> script_code_to_exec;
std::vector<std::string> script_prefixes = { "" }; // "" = "no prefix"
int supervised_workers = 0;
@ -239,16 +240,16 @@ struct zeek_options {
bool perftools_check_leaks = false;
bool perftools_profile = false;
std::string pcap_filter;
std::optional<std::string> pcap_filter;
std::vector<std::string> interfaces;
std::vector<std::string> pcap_files;
std::vector<std::string> signature_files;
std::string pcap_output_file;
std::string random_seed_input_file;
std::string random_seed_output_file;
std::string process_status_file;
std::string zeekygen_config_file;
std::optional<std::string> pcap_output_file;
std::optional<std::string> random_seed_input_file;
std::optional<std::string> random_seed_output_file;
std::optional<std::string> process_status_file;
std::optional<std::string> zeekygen_config_file;
std::string libidmef_dtd_file = "idmef-message.dtd";
std::set<std::string> plugins_to_load;
@ -329,11 +330,6 @@ static zeek_options parse_cmdline(int argc, char** argv)
rval.debug_scripts = true;
break;
case 'e':
if ( optarg[0] == 0 )
// Cheating a bit, but allows checking for an empty string
// to determine whether -e was used or not.
rval.script_code_to_exec = " ";
else
rval.script_code_to_exec = optarg;
break;
case 'f':
@ -681,7 +677,7 @@ static std::vector<std::string> get_script_signature_files()
return rval;
}
static std::string get_exe_path(std::string invocation)
static std::string get_exe_path(const std::string& invocation)
{
if ( invocation.empty() )
return "";
@ -792,8 +788,8 @@ int main(int argc, char** argv)
options = {};
const auto& node_name = zeek::supervised_node->name;
if ( ! zeek::supervised_node->interface.empty() )
options.interfaces.emplace_back(zeek::supervised_node->interface);
if ( zeek::supervised_node->interface )
options.interfaces.emplace_back(*zeek::supervised_node->interface);
if ( ! zeek::supervised_node->cluster.empty() )
{
@ -838,17 +834,17 @@ int main(int argc, char** argv)
fprintf(stderr, "Zeek script debugging ON.\n");
}
if ( ! options.script_code_to_exec.empty() )
command_line_policy = options.script_code_to_exec.data();
if ( options.script_code_to_exec )
command_line_policy = options.script_code_to_exec->data();
if ( ! options.debug_script_tracing_file.empty() )
if ( options.debug_script_tracing_file )
{
g_trace_state.SetTraceFile(options.debug_script_tracing_file.data());
g_trace_state.SetTraceFile(options.debug_script_tracing_file->data());
g_trace_state.TraceOn();
}
if ( ! options.process_status_file.empty() )
proc_status_file = options.process_status_file.data();
if ( options.process_status_file )
proc_status_file = options.process_status_file->data();
atexit(atexit_handler);
set_processing_status("INITIALIZING", "main");
@ -862,9 +858,9 @@ int main(int argc, char** argv)
plugin_mgr = new plugin::Manager();
#ifdef DEBUG
if ( ! options.debug_log_streams.empty() )
if ( options.debug_log_streams )
{
debug_logger.EnableStreams(options.debug_log_streams.data());
debug_logger.EnableStreams(options.debug_log_streams->data());
const char* debug_log_name = nullptr;
if ( ! getenv("ZEEK_DEBUG_LOG_STDERR") )
@ -896,11 +892,11 @@ int main(int argc, char** argv)
const char* seed_load_file = zeekenv("ZEEK_SEED_FILE");
if ( ! options.random_seed_input_file.empty() )
seed_load_file = options.random_seed_input_file.data();
if ( options.random_seed_input_file )
seed_load_file = options.random_seed_input_file->data();
init_random_seed((seed_load_file && *seed_load_file ? seed_load_file : 0),
options.random_seed_output_file.empty() ? 0 : options.random_seed_output_file.data());
options.random_seed_output_file ? options.random_seed_output_file->data() : 0);
// DEBUG_MSG("HMAC key: %s\n", md5_digest_print(shared_hmac_md5_key));
init_hash_function();
@ -929,8 +925,8 @@ int main(int argc, char** argv)
timer_mgr = new PQ_TimerMgr("<GLOBAL>");
// timer_mgr = new CQ_TimerMgr();
zeekygen_mgr = new zeekygen::Manager(options.zeekygen_config_file,
bro_argv[0]);
auto zeekygen_cfg = options.zeekygen_config_file.value_or("");
zeekygen_mgr = new zeekygen::Manager(zeekygen_cfg, bro_argv[0]);
add_essential_input_file("base/init-bare.zeek");
add_essential_input_file("base/init-frameworks-and-bifs.zeek");
@ -944,7 +940,7 @@ int main(int argc, char** argv)
options.script_options_to_set.empty() &&
options.pcap_files.size() == 0 &&
options.interfaces.size() == 0 &&
options.identifier_to_print.empty() &&
! options.identifier_to_print &&
! command_line_policy && ! options.print_plugins &&
! use_supervisor() && ! zeek::supervised_node )
add_input_file("-");
@ -1077,14 +1073,14 @@ int main(int argc, char** argv)
reporter->InitOptions();
zeekygen_mgr->GenerateDocs();
if ( ! options.pcap_filter.empty() )
if ( options.pcap_filter )
{
ID* id = global_scope()->Lookup("cmd_line_bpf_filter");
if ( ! id )
reporter->InternalError("global cmd_line_bpf_filter not defined");
id->SetVal(new StringVal(options.pcap_filter));
id->SetVal(new StringVal(*options.pcap_filter));
}
auto all_signature_files = options.signature_files;
@ -1164,11 +1160,11 @@ int main(int argc, char** argv)
}
// Print the ID.
if ( ! options.identifier_to_print.empty() )
if ( options.identifier_to_print )
{
ID* id = global_scope()->Lookup(options.identifier_to_print);
ID* id = global_scope()->Lookup(*options.identifier_to_print);
if ( ! id )
reporter->FatalError("No such ID: %s\n", options.identifier_to_print.data());
reporter->FatalError("No such ID: %s\n", options.identifier_to_print->data());
ODesc desc;
desc.SetQuotes(true);