Skip to content

Commit

Permalink
Support attaching interactive console to a running agent.
Browse files Browse the repository at this point in the history
By default, an agent now creates a UNIX socket for its console that an
external client can connect to. To connect to an already running
agent, execute `zeek-agent -r` on the same machine (note that client
and server must be running as the same user, probably `root`). Client
and server can specify a different socket path through `-s <PATH>`.
The environment variable `ZEEK_AGENT_SOCKET` can be set to specify the
path as well.

Remote consoles aren't support on Windows yet.

Internally, we switch the classic local console over to the same IPC
mechanism we now use for remote consoles, so that there's only one
implementation. For Windows, where actual IPC isn't implemented yet,
we provide a dummy implementation for this purpose that just forwards
data inside the main (and only) process.

Closes #8.
  • Loading branch information
rsmmr committed Jan 8, 2024
1 parent 5be001b commit f8a284d
Show file tree
Hide file tree
Showing 19 changed files with 1,286 additions and 238 deletions.
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Checks: 'bugprone-*,
readability-*,
-bugprone-easily-swappable-parameters,
-bugprone-unchecked-optional-access,
-cert-err58-cpp,
-clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-diagnostic-c++2a-designator,
Expand Down
43 changes: 36 additions & 7 deletions src/core/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,36 @@ using namespace zeek::agent;
options::LogLevel options::default_log_level = options::LogLevel::info;
options::LogType options::default_log_type = options::LogType::Stdout;
filesystem::path options::default_log_path = {};
filesystem::path options::default_socket_file_name = "zeek-agent.$$.sock";

static struct option long_driver_options[] = {
// clang-format off
{"autodoc", no_argument, nullptr, 'D'},
{"config", required_argument, nullptr, 'c'},
{"execute", required_argument, nullptr, 'e'},
{"help", no_argument, nullptr, 'h'},
{"interactive", no_argument, nullptr, 'i'},
{"log-level", required_argument, nullptr, 'L'},
{"autodoc", no_argument, nullptr, 'D'},
{"remote", no_argument, nullptr, 'r'},
{"socket", no_argument, nullptr, 's'},
{"terminate-on-disconnect", no_argument, nullptr, 'N'},
{"test", no_argument, nullptr, 'T'},
{"use-mock-data", no_argument, nullptr, 'M'},
{"terminate-on-disconnect", no_argument, nullptr, 'N'},
{"zeek", required_argument, nullptr, 'z'},
{"version", no_argument, nullptr, 'v'},
{"zeek", required_argument, nullptr, 'z'},
{nullptr, 0, nullptr, 0}
// clang-format on
};

static void usage(const filesystem::path& name) {
auto cfg = platform::configurationFile() ? platform::configurationFile()->string() : std::string("n/a");

auto options = Options::default_();
std::string socket = "n/a";

if ( options.socket )
socket = options.socket->string();

// clang-format off
std::cerr << "\nUsage: " << name.filename().string() << frmt(
" [options]\n"
Expand All @@ -74,12 +83,14 @@ static void usage(const filesystem::path& name) {
" -N | --terminate-on-disconnect Terminate when remote side disconnects (for testing)\n"
" -T | --test Run unit tests and exit\n"
" -c | --config <FILE> Load configuration from file [default: {}]\n"
" -e | --execute <STMT> SQL statement to execute immediately, then quit"
" -e | --execute <STMT> SQL statement to execute immediately, then quit\n"
" -h | --help Show usage information\n"
" -i | --interactive Spawn interactive console\n"
" -r | --remote Connect interactive console to already running agent\n"
" -s | --socket <FILE> Specify socket to use for console communication [default: {}]\n"
" -v | --version Print version information\n"
" -z | --zeek <host>[:port] Connect to Zeek at given address\n"
"\n", cfg);
"\n", cfg, socket);
// clang-format on
}

Expand All @@ -101,8 +112,8 @@ Result<Nothing> Options::parseArgv(const std::vector<std::string>& argv) {
#endif

while ( true ) {
int c =
getopt_long(static_cast<int>(argv_.size()), argv_.data(), "DL:MNTc:e:hivz:", long_driver_options, nullptr);
int c = getopt_long(static_cast<int>(argv_.size()), argv_.data(), "DL:MNTc:e:hirs:vz:", long_driver_options,
nullptr);
if ( c < 0 )
return Nothing();

Expand All @@ -129,6 +140,8 @@ Result<Nothing> Options::parseArgv(const std::vector<std::string>& argv) {
case 'c': config_file = optarg; break;
case 'e': execute = optarg; break;
case 'i': interactive = true; break;
case 'r': mode = options::Mode::RemoteConsole; break;
case 's': socket = filesystem::path(optarg); break;
case 'z': zeek_destinations.emplace_back(optarg); break;

case 'v': std::cerr << "Zeek Agent v" << VersionLong << std::endl; exit(0);
Expand All @@ -152,6 +165,7 @@ void Options::debugDump() const {
(log_level ? options::to_string(*log_level) : "<not set>"));
ZEEK_AGENT_DEBUG("configuration", "[option] log.type: {}", (log_type ? to_string(*log_type) : "<not set>"));
ZEEK_AGENT_DEBUG("configuration", "[option] log.path: {}", (log_path ? log_path->string() : "<not set>"));
ZEEK_AGENT_DEBUG("configuration", "[option] socket: {}", (socket ? socket->string() : "<not set>"));
ZEEK_AGENT_DEBUG("configuration", "[option] use-mock-data: {}", use_mock_data);
ZEEK_AGENT_DEBUG("configuration", "[option] terminate-on-disconnect: {}", terminate_on_disconnect);
ZEEK_AGENT_DEBUG("configuration", "[option] zeek.groups: {}", join(zeek_groups, ", "));
Expand Down Expand Up @@ -240,6 +254,21 @@ Options Options::default_() {
if ( path && filesystem::is_regular_file(*path) )
options.config_file = *path;

#ifndef HAVE_WINDOWS
if ( ! options.socket ) {
const char* env = getenv("ZEEK_AGENT_SOCKET");
if ( env && *env )
options.socket = env;
else {
filesystem::path socket_dir = "/tmp";
if ( auto d = platform::dataDirectory() )
socket_dir = *d;

options.socket = socket_dir / replace(options::default_socket_file_name, "$$", frmt("{}", getuid()));
}
}
#endif

return options;
}

Expand Down
21 changes: 17 additions & 4 deletions src/core/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ namespace options {
* couple of special modes beyond normal operation.
**/
enum class Mode {
Standard, /**< normal operation */
Test, /**< run unit tests and exit */
AutoDoc /**< print out JSON describing table schemas and exit */
Standard, /**< normal operation */
RemoteConsole, /**< connect to remote agent */
Test, /**< run unit tests and exit */
AutoDoc /**< print out JSON describing table schemas and exit */
};

inline std::string to_string(options::Mode mode) {
switch ( mode ) {
case options::Mode::RemoteConsole: return "remote console";
case options::Mode::Standard: return "standard";
case options::Mode::Test: return "test";
case options::Mode::AutoDoc: return "autodoc";
Expand Down Expand Up @@ -87,6 +89,7 @@ inline Result<LogType> from_str(const std::string_view& t) {
extern LogLevel default_log_level;
extern LogType default_log_type;
extern filesystem::path default_log_path;
extern filesystem::path default_socket_file_name;

} // namespace options

Expand Down Expand Up @@ -123,9 +126,16 @@ struct Options {
/** Console statement/command to execute at startup, and then terminate */
std::string execute;

/** True to spawn the interactive console */
/** True to spawn the interactive console locally. */
bool interactive = false;

/**
* Set to a socket name to spawn the interactive console connecting to a
* remote agent. If set to an empty path, expect remote at default socket
* location.
*/
std::optional<filesystem::path> interactive_remote;

/** The agent's level of logging. Default is `warn` and worse. */
std::optional<options::LogLevel> log_level;

Expand All @@ -135,6 +145,9 @@ struct Options {
/** File path associated with logger, if current type needs one. */
std::optional<filesystem::path> log_path = {};

/** Default socket for remote console. */
std::optional<filesystem::path> socket;

/** True to have any tables only report mock data for testing. */
bool use_mock_data = false;

Expand Down
Loading

0 comments on commit f8a284d

Please sign in to comment.