Aggressively disallow a TTY stdin for assemble_cvd and run_cvd.

These processes expect to be passed lists of files on stdin and have
stdin close on completion of the list. A TTY will most likely not pass a
list of files and then close the file descriptor.

This supports the following invocations:

$ fetch_cvd -run_next_stage
$ fetch_cvd | launch_cvd
$ launch_cvd
$ fetch_cvd | assemble_cvd | run_cvd

Bug: 135293952
Test: Run launch_cvd, assemble_cvd, run_cvd
Change-Id: I203b5c4f620e3b2955408672a32e7df0d47e879b
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index 4e434c5..9efc0dd 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -23,6 +23,19 @@
 int main(int argc, char** argv) {
   ::android::base::InitLogging(argv, android::base::StderrLogger);
 
+  if (isatty(0)) {
+    LOG(FATAL) << "stdin was a tty, expected to be passed the output of a previous stage. "
+               << "Did you mean to run launch_cvd?";
+    return cvd::AssemblerExitCodes::kInvalidHostConfiguration;
+  } else {
+    int error_num = errno;
+    if (error_num == EBADF) {
+      LOG(FATAL) << "stdin was not a valid file descriptor, expected to be passed the output "
+                 << "of assemble_cvd. Did you mean to run launch_cvd?";
+      return cvd::AssemblerExitCodes::kInvalidHostConfiguration;
+    }
+  }
+
   auto config = InitFilesystemAndCreateConfig(&argc, &argv);
 
   std::cout << GetConfigFilePath(*config) << "\n";
diff --git a/host/commands/launch/launch_cvd.cc b/host/commands/launch/launch_cvd.cc
index 857b471..11d07c1 100644
--- a/host/commands/launch/launch_cvd.cc
+++ b/host/commands/launch/launch_cvd.cc
@@ -22,17 +22,38 @@
 
 #include "flag_forwarder.h"
 
+/**
+ * If stdin is a tty, that means a user is invoking launch_cvd on the command
+ * line and wants automatic file detection for assemble_cvd.
+ *
+ * If stdin is not a tty, that means launch_cvd is being passed a list of files
+ * and that list should be forwarded to assemble_cvd.
+ *
+ * Controllable with a flag for extraordinary scenarios such as running from a
+ * daemon which closes its own stdin.
+ */
+DEFINE_bool(run_file_discovery, (bool) isatty(0),
+            "Whether to run file discovery or get input files from stdin.");
+
 namespace {
 
 std::string kAssemblerBin = vsoc::DefaultHostArtifactsPath("bin/assemble_cvd");
 std::string kRunnerBin = vsoc::DefaultHostArtifactsPath("bin/run_cvd");
 
-cvd::Subprocess StartAssembler(cvd::SharedFD assembler_stdout,
+void AvailableFilesReport(cvd::SharedFD) {
+  // TODO(schuffelen): Scan directories like ANDROID_PRODUCT_OUT, ANDROID_HOST_OUT, HOME
+}
+
+cvd::Subprocess StartAssembler(cvd::SharedFD assembler_stdin,
+                               cvd::SharedFD assembler_stdout,
                                const std::vector<std::string>& argv) {
   cvd::Command assemble_cmd(kAssemblerBin);
   for (const auto& arg : argv) {
     assemble_cmd.AddParameter(arg);
   }
+  if (assembler_stdin->IsOpen()) {
+    assemble_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdIn, assembler_stdin);
+  }
   assemble_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, assembler_stdout);
   return assemble_cmd.Start();
 }
@@ -63,13 +84,24 @@
   cvd::SharedFD assembler_stdout, runner_stdin;
   cvd::SharedFD::Pipe(&runner_stdin, &assembler_stdout);
 
+  cvd::SharedFD launcher_report, assembler_stdin;
+  bool should_generate_report = FLAGS_run_file_discovery;
+  if (should_generate_report) {
+    cvd::SharedFD::Pipe(&assembler_stdin, &launcher_report);
+  }
+
   // SharedFDs are std::move-d in to avoid dangling references.
   // Removing the std::move will probably make run_cvd hang as its stdin never closes.
-  auto assemble_proc = StartAssembler(std::move(assembler_stdout),
+  auto assemble_proc = StartAssembler(std::move(assembler_stdin),
+                                      std::move(assembler_stdout),
                                       forwarder.ArgvForSubprocess(kAssemblerBin));
   auto run_proc = StartRunner(std::move(runner_stdin),
                               forwarder.ArgvForSubprocess(kRunnerBin));
 
+  if (should_generate_report) {
+    AvailableFilesReport(std::move(launcher_report));
+  }
+
   auto assemble_ret = assemble_proc.Wait();
   if (assemble_ret != 0) {
     LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 55b8704..5ea68a3 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -324,6 +324,19 @@
   ::android::base::InitLogging(argv, android::base::StderrLogger);
   google::ParseCommandLineFlags(&argc, &argv, false);
 
+  if (isatty(0)) {
+    LOG(FATAL) << "stdin was a tty, expected to be passed the output of a previous stage. "
+               << "Did you mean to run launch_cvd?";
+    return cvd::RunnerExitCodes::kInvalidHostConfiguration;
+  } else {
+    int error_num = errno;
+    if (error_num == EBADF) {
+      LOG(FATAL) << "stdin was not a valid file descriptor, expected to be passed the output "
+                 << "of assemble_cvd. Did you mean to run launch_cvd?";
+      return cvd::RunnerExitCodes::kInvalidHostConfiguration;
+    }
+  }
+
   std::string input_files_str;
   {
     auto input_fd = cvd::SharedFD::Dup(0);