Check that TAP devices aren't in use.

This is just a sanity check at the moment to avoid multiple VMMs using
the same TAP device. Looking at the list of tap devices in use will in
the future help with selecting available instance numbers / indexes to
run a device in.

Prior to this change, if the TAP device was already being used,
run_cvd would run without that tap device and print
"Unable to connect to ..tap interface: Device or resource busy".
This makes run_cvd fail outright if the tap device is already being used
by another process.

Test: Build and run when TAP device is in use (through vde_plug2tap)
Change-Id: If233470bbae6fdbcb2956f618f350ca2dec7f4ff
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index f4943ed..2dbf3a4 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -20,8 +20,11 @@
 #include <linux/if_tun.h>
 #include <string.h>
 
+#include <android-base/strings.h>
 #include "common/libs/glog/logging.h"
 
+#include "common/libs/utils/subprocess.h"
+
 namespace cvd {
 namespace {
 // This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
@@ -74,4 +77,22 @@
 
   return tap_fd;
 }
+
+std::set<std::string> TapInterfacesInUse() {
+  Command cmd("/bin/bash");
+  cmd.AddParameter("-c");
+  cmd.AddParameter("egrep -h -e \"^iff:.*\" /proc/*/fdinfo/*");
+  std::string stdin, stdout, stderr;
+  RunWithManagedStdio(std::move(cmd), &stdin, &stdout, &stderr);
+  auto lines = android::base::Split(stdout, "\n");
+  std::set<std::string> tap_interfaces;
+  for (const auto& line : lines) {
+    if (!android::base::StartsWith(line, "iff:\t")) {
+      LOG(ERROR) << "Unexpected line \"" << line << "\"";
+      continue;
+    }
+    tap_interfaces.insert(line.substr(std::string("iff:\t").size()));
+  }
+  return tap_interfaces;
+}
 }  // namespace cvd
diff --git a/common/libs/utils/network.h b/common/libs/utils/network.h
index 7b856e4..221d88e 100644
--- a/common/libs/utils/network.h
+++ b/common/libs/utils/network.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <set>
 #include <string>
 
 #include "common/libs/fs/shared_fd.h"
@@ -24,4 +25,7 @@
 // user needs CAP_NET_ADMIN to create such interfaces or be the owner to connect
 // to one.
 SharedFD OpenTapInterface(const std::string& interface_name);
-}
\ No newline at end of file
+
+// Returns a list of TAP devices that have open file descriptors
+std::set<std::string> TapInterfacesInUse();
+}
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index c77e924..4019b7c 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -45,6 +45,7 @@
 #include "common/libs/fs/tee.h"
 #include "common/libs/utils/environment.h"
 #include "common/libs/utils/files.h"
+#include "common/libs/utils/network.h"
 #include "common/libs/utils/subprocess.h"
 #include "common/libs/utils/size_utils.h"
 #include "host/commands/run_cvd/kernel_args.h"
@@ -331,6 +332,15 @@
     return RunnerExitCodes::kInstanceDirCreationError;
   }
 
+  auto used_tap_devices = cvd::TapInterfacesInUse();
+  if (used_tap_devices.count(config->wifi_tap_name())) {
+    LOG(ERROR) << "Wifi TAP device already in use";
+    return RunnerExitCodes::kTapDeviceInUse;
+  } else if (used_tap_devices.count(config->mobile_tap_name())) {
+    LOG(ERROR) << "Mobile TAP device already in use";
+    return RunnerExitCodes::kTapDeviceInUse;
+  }
+
   auto vm_manager = vm_manager::VmManager::Get(config->vm_manager(), config);
 
   // Check host configuration
diff --git a/host/commands/run_cvd/runner_defs.h b/host/commands/run_cvd/runner_defs.h
index 1486b11..daf8868 100644
--- a/host/commands/run_cvd/runner_defs.h
+++ b/host/commands/run_cvd/runner_defs.h
@@ -44,6 +44,7 @@
   kTombstoneServerError = 20,
   kTombstoneDirCreationError = 21,
   kInitRamFsConcatError = 22,
+  kTapDeviceInUse = 23,
 };
 
 // Actions supported by the launcher server