diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index ebeaed7..cd6dad5 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -663,9 +663,9 @@ cc_test( name = "namespace_test", srcs = ["namespace_test.cc"], copts = sapi_platform_copts(), - data = [ - "//sandboxed_api/sandbox2/testcases:hostname", - "//sandboxed_api/sandbox2/testcases:namespace", + data = ["//sandboxed_api/sandbox2/testcases:namespace"], + tags = [ + "requires-net:external", ], deps = [ ":namespace", @@ -1009,17 +1009,8 @@ cc_test( name = "policybuilder_test", srcs = ["policybuilder_test.cc"], copts = sapi_platform_copts(), - tags = [ - "no_qemu_user_mode", - "requires-net:external", - ], deps = [ - ":comms", ":policybuilder", - ":sandbox2", - ":testonly_allow_all_syscalls", - "//sandboxed_api:config", - "//sandboxed_api:testing", "//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/util:status_matchers", "@com_google_absl//absl/log", diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index d54d5c5..9f3a9a2 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -800,7 +800,6 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING) OUTPUT_NAME namespace_test ) add_dependencies(sandbox2_namespace_test - sandbox2::testcase_hostname sandbox2::testcase_namespace ) target_link_libraries(sandbox2_namespace_test PRIVATE @@ -1109,11 +1108,11 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING) ) target_link_libraries(sandbox2_policybuilder_test PRIVATE absl::strings - sandbox2::allow_all_syscalls + absl::log + absl::status + absl::statusor sandbox2::bpf_helper - sandbox2::comms sandbox2::policybuilder - sandbox2::sandbox2 sapi::testing sapi::status_matchers sapi::test_main diff --git a/sandboxed_api/sandbox2/namespace_test.cc b/sandboxed_api/sandbox2/namespace_test.cc index ea7c4be..d652032 100644 --- a/sandboxed_api/sandbox2/namespace_test.cc +++ b/sandboxed_api/sandbox2/namespace_test.cc @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -47,28 +49,48 @@ using ::sapi::CreateDefaultPermissiveTestPolicy; using ::sapi::CreateNamedTempFile; using ::sapi::GetTestSourcePath; using ::sapi::GetTestTempPath; +using ::testing::Contains; +using ::testing::ElementsAre; using ::testing::Eq; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::IsEmpty; using ::testing::Ne; +using ::testing::SizeIs; +using ::testing::StartsWith; +using ::testing::StrEq; // sapi::google3-only(broken matchers) std::string GetTestcaseBinPath(absl::string_view bin_name) { return GetTestSourcePath(absl::StrCat("sandbox2/testcases/", bin_name)); } -int RunSandboxeeWithArgsAndPolicy(const std::string& bin_path, - std::initializer_list args, - std::unique_ptr policy = nullptr) { +std::vector RunSandboxeeWithArgsAndPolicy( + const std::string& bin_path, std::initializer_list args, + std::unique_ptr policy = nullptr) { if (!policy) { policy = CreateDefaultPermissiveTestPolicy(bin_path).BuildOrDie(); } Sandbox2 sandbox(std::make_unique(bin_path, args), std::move(policy)); - Result result = sandbox.Run(); - EXPECT_THAT(result.final_status(), Eq(Result::OK)); - return result.reason_code(); -} + CHECK(sandbox.RunAsync()); + Comms* comms = sandbox.comms(); + uint64_t num; -constexpr absl::string_view kHostnameTestBinary = "sandbox2/testcases/hostname"; + std::vector entries; + if (comms->RecvUint64(&num)) { + entries.reserve(num); + for (int i = 0; i < num; ++i) { + std::string entry; + CHECK(comms->RecvString(&entry)); + entries.push_back(std::move(entry)); + } + } + Result result = sandbox.AwaitResult(); + EXPECT_THAT(result.final_status(), Eq(Result::OK)); + EXPECT_THAT(result.reason_code(), Eq(0)); + return entries; +} TEST(NamespaceTest, FileNamespaceWorks) { // Mount /binary_path RO and check that it exists and is readable. @@ -78,9 +100,9 @@ TEST(NamespaceTest, FileNamespaceWorks) { SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path) .AddFileAt(path, "/binary_path") .TryBuild()); - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "0", "/binary_path", "/etc/passwd"}, std::move(policy)); - EXPECT_THAT(reason_code, Eq(2)); + EXPECT_THAT(result, ElementsAre("/binary_path")); } TEST(NamespaceTest, ReadOnlyIsRespected) { @@ -95,9 +117,9 @@ TEST(NamespaceTest, ReadOnlyIsRespected) { .AddFileAt(name, "/temp_file") .TryBuild()); // Check that it is readable - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "0", "/temp_file"}, std::move(policy)); - EXPECT_THAT(reason_code, Eq(0)); + EXPECT_THAT(result, ElementsAre("/temp_file")); } { SAPI_ASSERT_OK_AND_ASSIGN(auto policy, @@ -105,9 +127,9 @@ TEST(NamespaceTest, ReadOnlyIsRespected) { .AddFileAt(name, "/temp_file") .TryBuild()); // Now check that it is not writeable - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "1", "/temp_file"}, std::move(policy)); - EXPECT_THAT(reason_code, Eq(1)); + EXPECT_THAT(result, IsEmpty()); } } @@ -116,19 +138,20 @@ TEST(NamespaceTest, UserNamespaceWorks) { // Check that getpid() returns 2 (which is the case inside pid NS). { - int reason_code = RunSandboxeeWithArgsAndPolicy(path, {path, "2"}); - EXPECT_THAT(reason_code, Eq(0)); + std::vector result = + RunSandboxeeWithArgsAndPolicy(path, {path, "2"}); + EXPECT_THAT(result, ElementsAre("2")); } // Validate that getpid() does not return 2 when outside of a pid NS. { - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "2"}, PolicyBuilder() .DisableNamespaces() .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls .BuildOrDie()); - EXPECT_THAT(reason_code, Ne(0)); + EXPECT_THAT(result, ElementsAre(Ne("2"))); } } @@ -137,20 +160,21 @@ TEST(NamespaceTest, UserNamespaceIDMapWritten) { // started. const std::string path = GetTestcaseBinPath("namespace"); { - int reason_code = + std::vector result = RunSandboxeeWithArgsAndPolicy(path, {path, "3", "1000", "1000"}); - EXPECT_THAT(reason_code, Eq(0)); + EXPECT_THAT(result, ElementsAre("1000", "1000")); } // Check that the uid/gid is the same when not using namespaces. { - int reason_code = RunSandboxeeWithArgsAndPolicy( - path, {path, "3", absl::StrCat(getuid()), absl::StrCat(getgid())}, + std::vector result = RunSandboxeeWithArgsAndPolicy( + path, {path, "3"}, PolicyBuilder() .DisableNamespaces() .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls .BuildOrDie()); - EXPECT_THAT(reason_code, Eq(0)); + EXPECT_THAT(result, + ElementsAre(absl::StrCat(getuid()), absl::StrCat(getgid()))); } } @@ -162,9 +186,9 @@ TEST(NamespaceTest, RootReadOnly) { auto policy, CreateDefaultPermissiveTestPolicy(path) .AddTmpfs("/tmp", /*size=*/4ULL << 20 /* 4 MiB */) .TryBuild()); - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "4", "/tmp/testfile", "/testfile"}, std::move(policy)); - EXPECT_THAT(reason_code, Eq(2)); + EXPECT_THAT(result, ElementsAre("/tmp/testfile")); } TEST(NamespaceTest, RootWritable) { @@ -173,36 +197,58 @@ TEST(NamespaceTest, RootWritable) { SAPI_ASSERT_OK_AND_ASSIGN( auto policy, CreateDefaultPermissiveTestPolicy(path).SetRootWritable().TryBuild()); - int reason_code = RunSandboxeeWithArgsAndPolicy( + std::vector result = RunSandboxeeWithArgsAndPolicy( path, {path, "4", "/testfile"}, std::move(policy)); - EXPECT_THAT(reason_code, Eq(0)); + EXPECT_THAT(result, ElementsAre("/testfile")); } -TEST(HostnameTest, None) { - const std::string path = GetTestcaseBinPath("hostname"); - int reason_code = RunSandboxeeWithArgsAndPolicy( - path, {path, "sandbox2"}, +TEST(NamespaceTest, HostnameNone) { + const std::string path = GetTestcaseBinPath("namespace"); + std::vector result = RunSandboxeeWithArgsAndPolicy( + path, {path, "7"}, PolicyBuilder() .DisableNamespaces() .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls .BuildOrDie()); - EXPECT_THAT(reason_code, Eq(1)); + EXPECT_THAT(result, ElementsAre(Ne("sandbox2"))); } -TEST(HostnameTest, Default) { - const std::string path = GetTestcaseBinPath("hostname"); - int reason_code = RunSandboxeeWithArgsAndPolicy(path, {path, "sandbox2"}); - EXPECT_THAT(reason_code, Eq(0)); +TEST(NamespaceTest, HostnameDefault) { + const std::string path = GetTestcaseBinPath("namespace"); + std::vector result = + RunSandboxeeWithArgsAndPolicy(path, {path, "7"}); + EXPECT_THAT(result, ElementsAre("sandbox2")); } -TEST(HostnameTest, Configured) { - const std::string path = GetTestcaseBinPath("hostname"); +TEST(NamespaceTest, HostnameConfigured) { + const std::string path = GetTestcaseBinPath("namespace"); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path) .SetHostname("configured") .TryBuild()); - int reason_code = RunSandboxeeWithArgsAndPolicy(path, {path, "configured"}, - std::move(policy)); - EXPECT_THAT(reason_code, Eq(0)); + std::vector result = + RunSandboxeeWithArgsAndPolicy(path, {path, "7"}, std::move(policy)); + EXPECT_THAT(result, ElementsAre("configured")); +} + +TEST(NamespaceTest, TestInterfacesNoNetwork) { + const std::string path = GetTestcaseBinPath("namespace"); + std::vector result = + RunSandboxeeWithArgsAndPolicy(path, {path, "5"}); + // Only loopback network interface 'lo'. + EXPECT_THAT(result, ElementsAre("lo")); +} + +TEST(NamespaceTest, TestInterfacesWithNetwork) { + const std::string path = GetTestcaseBinPath("namespace"); + SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path) + .AllowUnrestrictedNetworking() + .TryBuild()); + + std::vector result = + RunSandboxeeWithArgsAndPolicy(path, {path, "5"}, std::move(policy)); + // Loopback network interface 'lo' and more. + EXPECT_THAT(result, Contains("lo")); + EXPECT_THAT(result, SizeIs(Gt(1))); } } // namespace diff --git a/sandboxed_api/sandbox2/policybuilder_test.cc b/sandboxed_api/sandbox2/policybuilder_test.cc index 64bd834..c328562 100644 --- a/sandboxed_api/sandbox2/policybuilder_test.cc +++ b/sandboxed_api/sandbox2/policybuilder_test.cc @@ -26,18 +26,7 @@ #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "sandboxed_api/config.h" -#include "sandboxed_api/sandbox2/allow_all_syscalls.h" -#include "sandboxed_api/sandbox2/comms.h" -#include "sandboxed_api/sandbox2/executor.h" -#include "sandboxed_api/sandbox2/ipc.h" -#include "sandboxed_api/sandbox2/result.h" -#include "sandboxed_api/sandbox2/sandbox2.h" #include "sandboxed_api/sandbox2/util/bpf_helper.h" -#include "sandboxed_api/testing.h" #include "sandboxed_api/util/status_matchers.h" namespace sandbox2 { @@ -59,24 +48,14 @@ class PolicyBuilderPeer { namespace { -using ::testing::AllOf; -using ::testing::AnyOf; using ::testing::Eq; -using ::testing::Gt; -using ::testing::HasSubstr; using ::testing::Lt; using ::testing::StartsWith; using ::testing::StrEq; using ::sapi::IsOk; using ::sapi::StatusIs; -class PolicyBuilderTest : public testing::Test { - protected: - static std::string Run(const std::vector& args, - bool network = false); -}; - -TEST_F(PolicyBuilderTest, Testpolicy_size) { +TEST(PolicyBuilderTest, Testpolicy_size) { ssize_t last_size = 0; PolicyBuilder builder; PolicyBuilderPeer builder_peer{&builder}; @@ -131,7 +110,7 @@ TEST_F(PolicyBuilderTest, Testpolicy_size) { // clang-format on } -TEST_F(PolicyBuilderTest, TestValidateAbsolutePath) { +TEST(PolicyBuilderTest, TestValidateAbsolutePath) { for (auto const& bad_path : { "..", "a", @@ -155,52 +134,14 @@ TEST_F(PolicyBuilderTest, TestValidateAbsolutePath) { } } -std::string PolicyBuilderTest::Run(const std::vector& args, - bool network) { - PolicyBuilder builder; - // Don't restrict the syscalls at all. - builder.DefaultAction(AllowAllSyscalls()); - - if constexpr (sapi::host_os::IsAndroid()) { - builder.DisableNamespaces(); - } else { - builder.AddLibrariesForBinary(args[0]); - } - - if (network) { - builder.AllowUnrestrictedNetworking(); - } - - auto executor = std::make_unique(args[0], args); - int fd1 = executor->ipc()->ReceiveFd(STDOUT_FILENO); - sandbox2::Sandbox2 s2(std::move(executor), builder.BuildOrDie()); - - s2.RunAsync(); - - char buf[4096]; - std::string output; - - while (true) { - int nbytes; - PCHECK((nbytes = read(fd1, buf, sizeof(buf))) >= 0); - - if (nbytes == 0) break; - output += std::string(buf, nbytes); - } - - auto result = s2.AwaitResult(); - EXPECT_EQ(result.final_status(), sandbox2::Result::OK); - return output; -} - -TEST_F(PolicyBuilderTest, TestCanOnlyBuildOnce) { +TEST(PolicyBuilderTest, TestCanOnlyBuildOnce) { PolicyBuilder b; ASSERT_THAT(b.TryBuild(), IsOk()); EXPECT_THAT(b.TryBuild(), StatusIs(absl::StatusCode::kFailedPrecondition, "Can only build policy once.")); } -TEST_F(PolicyBuilderTest, TestIsCopyable) { +TEST(PolicyBuilderTest, TestIsCopyable) { PolicyBuilder builder; builder.AllowSyscall(__NR_getpid); @@ -212,40 +153,5 @@ TEST_F(PolicyBuilderTest, TestIsCopyable) { EXPECT_THAT(builder.TryBuild(), IsOk()); EXPECT_THAT(copy.TryBuild(), IsOk()); } - -TEST_F(PolicyBuilderTest, TestEcho) { - ASSERT_THAT(Run({"/bin/echo", "HELLO"}), StrEq("HELLO\n")); -} - -TEST_F(PolicyBuilderTest, TestInterfacesNoNetwork) { - SKIP_ANDROID; - auto lines = absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}), '\n'); - - int count = 0; - for (auto const& line : lines) { - if (!line.empty() && !absl::StartsWith(line, " ")) { - count += 1; - } - } - - // Only loopback network interface 'lo'. - EXPECT_THAT(count, Eq(1)); -} - -TEST_F(PolicyBuilderTest, TestInterfacesNetwork) { - SKIP_ANDROID; - auto lines = - absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}, true), '\n'); - - int count = 0; - for (auto const& line : lines) { - if (!line.empty() && !absl::StartsWith(line, " ")) { - count += 1; - } - } - - // Loopback network interface 'lo' and more. - EXPECT_THAT(count, Gt(1)); -} } // namespace } // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/testcases/BUILD.bazel b/sandboxed_api/sandbox2/testcases/BUILD.bazel index 41e291e..0c71d1a 100644 --- a/sandboxed_api/sandbox2/testcases/BUILD.bazel +++ b/sandboxed_api/sandbox2/testcases/BUILD.bazel @@ -213,14 +213,6 @@ cc_binary( features = ["fully_static_link"], ) -cc_binary( - name = "hostname", - testonly = True, - srcs = ["hostname.cc"], - copts = sapi_platform_copts(), - features = ["fully_static_link"], -) - cc_binary( name = "limits", testonly = True, @@ -235,4 +227,11 @@ cc_binary( srcs = ["namespace.cc"], copts = sapi_platform_copts(), features = ["fully_static_link"], + deps = [ + "//sandboxed_api/sandbox2:comms", + "//sandboxed_api/util:file_base", + "//sandboxed_api/util:fileops", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/log:check", + ], ) diff --git a/sandboxed_api/sandbox2/testcases/CMakeLists.txt b/sandboxed_api/sandbox2/testcases/CMakeLists.txt index cd035b9..4f0fc19 100644 --- a/sandboxed_api/sandbox2/testcases/CMakeLists.txt +++ b/sandboxed_api/sandbox2/testcases/CMakeLists.txt @@ -261,19 +261,6 @@ target_link_libraries(sandbox2_testcase_tsync PRIVATE sapi::base ) -# sandboxed_api/sandbox2/testcases:hostname -add_executable(sandbox2_testcase_hostname - hostname.cc -) -add_executable(sandbox2::testcase_hostname ALIAS sandbox2_testcase_hostname) -set_target_properties(sandbox2_testcase_hostname PROPERTIES - OUTPUT_NAME hostname -) -target_link_libraries(sandbox2_testcase_hostname PRIVATE - -static - sapi::base -) - # sandboxed_api/sandbox2/testcases:limits add_executable(sandbox2_testcase_limits limits.cc @@ -297,5 +284,10 @@ set_target_properties(sandbox2_testcase_namespace PROPERTIES ) target_link_libraries(sandbox2_testcase_namespace PRIVATE -static + absl::check + absl::flat_hash_set + sandbox2::comms sapi::base + sapi::file_base + sapi::fileops ) diff --git a/sandboxed_api/sandbox2/testcases/hostname.cc b/sandboxed_api/sandbox2/testcases/hostname.cc deleted file mode 100644 index ff59620..0000000 --- a/sandboxed_api/sandbox2/testcases/hostname.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// A binary to test network namespace hostname. -// Usage: ./hostname -// Success only if the hostname is as expected. - -#include - -#include -#include -#include -#include - -int main(int argc, char* argv[]) { - if (argc < 2) { - printf("argc < 2\n"); - return EXIT_FAILURE; - } - - char hostname[1024]; - if (gethostname(hostname, sizeof(hostname)) == -1) { - printf("gethostname: error %d\n", errno); - return EXIT_FAILURE; - } - - if (strcmp(hostname, argv[1]) != 0) { - printf("gethostname: got %s, want %s\n", hostname, argv[1]); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/sandboxed_api/sandbox2/testcases/namespace.cc b/sandboxed_api/sandbox2/testcases/namespace.cc index ed7d08e..725addd 100644 --- a/sandboxed_api/sandbox2/testcases/namespace.cc +++ b/sandboxed_api/sandbox2/testcases/namespace.cc @@ -29,11 +29,48 @@ // Create provided files, return 0 on OK. // Returns the index of the first non-creatable file on failure. #include +#include #include #include #include #include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "absl/log/check.h" +#include "sandboxed_api/sandbox2/comms.h" +#include "sandboxed_api/util/fileops.h" +#include "sandboxed_api/util/path.h" + +namespace { + +using sapi::file::JoinPath; +using sapi::file_util::fileops::ListDirectoryEntries; + +bool IsDirectory(const std::string& path) { + struct stat statbuf; + PCHECK(stat(path.c_str(), &statbuf) == 0); + return statbuf.st_mode & S_IFDIR; +} + +void ListDirectoriesRecursively(const std::string& path, + std::vector& files) { + std::string error; + std::vector entries; + CHECK(ListDirectoryEntries(path, &entries, &error)) << error; + for (const std::string& entry : entries) { + std::string new_path = JoinPath(path, entry); + if (IsDirectory(new_path)) { + ListDirectoriesRecursively(new_path, files); + } else { + files.push_back(new_path); + } + } +} + +} // namespace int main(int argc, char* argv[]) { if (argc < 2) { @@ -41,53 +78,78 @@ int main(int argc, char* argv[]) { } int mode = atoi(argv[1]); // NOLINT(runtime/deprecated_fn) + std::vector result; + + sandbox2::Comms comms(sandbox2::Comms::kDefaultConnection); switch (mode) { - case 0: { + case 0: // Make sure file exist for (int i = 2; i < argc; i++) { - if (access(argv[i], R_OK)) { - return i - 1; - } - } - } break; - - case 1: { - for (int i = 2; i < argc; i++) { - if (access(argv[i], W_OK)) { - return i - 1; - } - } - } break; - - case 2: { - if (getpid() != 2) { - return -1; - } - } break; - - case 3: { - if (argc != 4) { - return 1; - } - - if (getuid() != atoi(argv[2]) // NOLINT(runtime/deprecated_fn) - || getgid() != atoi(argv[3])) { // NOLINT(runtime/deprecated_fn) - return -1; - } - } break; - - case 4: - for (int i = 2; i < argc; ++i) { - if (open(argv[i], O_CREAT | O_WRONLY, 0644) == -1) { - return i - 1; + if (access(argv[i], R_OK) == 0) { + result.push_back(argv[i]); } } break; + case 1: + for (int i = 2; i < argc; i++) { + if (access(argv[i], W_OK) == 0) { + result.push_back(argv[i]); + } + } + break; + + case 2: + result.push_back(absl::StrCat(getpid())); + break; + + case 3: + result.push_back(absl::StrCat(getuid())); + result.push_back(absl::StrCat(getgid())); + break; + + case 4: + for (int i = 2; i < argc; ++i) { + if (open(argv[i], O_CREAT | O_WRONLY, 0644) != -1) { + result.push_back(argv[i]); + } + } + break; + + case 5: { + absl::flat_hash_set ifnames; + struct ifaddrs* addrs; + if (getifaddrs(&addrs)) { + return -1; + } + for (struct ifaddrs* cur = addrs; cur; cur = cur->ifa_next) { + ifnames.insert(cur->ifa_name); + } + result.insert(result.end(), ifnames.begin(), ifnames.end()); + freeifaddrs(addrs); + break; + } + + case 6: + ListDirectoriesRecursively(argv[2], result); + break; + case 7: { + char hostname[1000]; + if (gethostname(hostname, sizeof(hostname)) == -1) { + return -1; + } + result.push_back(hostname); + break; + } + default: return 1; } + CHECK(comms.SendUint64(result.size())); + for (const std::string& entry : result) { + CHECK(comms.SendString(entry)); + } return 0; }