Add cvd_status to check on the launcher

Bug: 143232127
Test: Run cvd_status when the device is on and off
Change-Id: Id08c08b34e81abc45c508d8c44a2f55d01f3b3bc
diff --git a/host/commands/cvd_status/Android.bp b/host/commands/cvd_status/Android.bp
new file mode 100644
index 0000000..7196e07
--- /dev/null
+++ b/host/commands/cvd_status/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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
+//
+//      http://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.
+
+cc_binary_host {
+    name: "cvd_status",
+    srcs: [
+        "cvd_status.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "cuttlefish_auto_resources",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libcuttlefish_vm_manager",
+        "libjsoncpp",
+        "libgflags",
+        "libxml2",
+    ],
+    defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
+}
diff --git a/host/commands/cvd_status/cvd_status.cc b/host/commands/cvd_status/cvd_status.cc
new file mode 100644
index 0000000..fb9911f
--- /dev/null
+++ b/host/commands/cvd_status/cvd_status.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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.
+ */
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <fstream>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/utils/environment.h"
+#include "host/commands/run_cvd/runner_defs.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+DEFINE_int32(wait_for_launcher, 5,
+             "How many seconds to wait for the launcher to respond to the status "
+             "command. A value of zero means wait indefinetly");
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (!config) {
+    LOG(ERROR) << "Failed to obtain config object";
+    return 1;
+  }
+
+  auto monitor_path = config->launcher_monitor_socket_path();
+  if (monitor_path.empty()) {
+    LOG(ERROR) << "No path to launcher monitor found";
+    return 2;
+  }
+  auto monitor_socket = cvd::SharedFD::SocketLocalClient(monitor_path.c_str(),
+                                                         false, SOCK_STREAM);
+  if (!monitor_socket->IsOpen()) {
+    LOG(ERROR) << "Unable to connect to launcher monitor at " << monitor_path
+               << ": " << monitor_socket->StrError();
+    return 3;
+  }
+  auto request = cvd::LauncherAction::kStatus;
+  auto bytes_sent = monitor_socket->Send(&request, sizeof(request), 0);
+  if (bytes_sent < 0) {
+    LOG(ERROR) << "Error sending launcher monitor the status command: "
+               << monitor_socket->StrError();
+    return 4;
+  }
+  // Perform a select with a timeout to guard against launcher hanging
+  cvd::SharedFDSet read_set;
+  read_set.Set(monitor_socket);
+  struct timeval timeout = {FLAGS_wait_for_launcher, 0};
+  int selected = cvd::Select(&read_set, nullptr, nullptr,
+                             FLAGS_wait_for_launcher <= 0 ? nullptr : &timeout);
+  if (selected < 0){
+    LOG(ERROR) << "Failed communication with the launcher monitor: "
+               << strerror(errno);
+    return 5;
+  }
+  if (selected == 0) {
+    LOG(ERROR) << "Timeout expired waiting for launcher monitor to respond";
+    return 6;
+  }
+  cvd::LauncherResponse response;
+  auto bytes_recv = monitor_socket->Recv(&response, sizeof(response), 0);
+  if (bytes_recv < 0) {
+    LOG(ERROR) << "Error receiving response from launcher monitor: "
+               << monitor_socket->StrError();
+    return 7;
+  }
+  if (response != cvd::LauncherResponse::kSuccess) {
+    LOG(ERROR) << "Received '" << static_cast<char>(response)
+               << "' response from launcher monitor";
+    return 8;
+  }
+  LOG(INFO) << "run_cvd is active.";
+  return 0;
+}
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 5ea68a3..663b7f0 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -304,6 +304,12 @@
             client->Write(&response, sizeof(response));
           }
           break;
+        case cvd::LauncherAction::kStatus: {
+          // TODO(schuffelen): Return more information on a side channel
+          auto response = cvd::LauncherResponse::kSuccess;
+          client->Write(&response, sizeof(response));
+          break;
+        }
         default:
           LOG(ERROR) << "Unrecognized launcher action: "
                      << static_cast<char>(action);
diff --git a/host/commands/run_cvd/runner_defs.h b/host/commands/run_cvd/runner_defs.h
index 255f5b0..1486b11 100644
--- a/host/commands/run_cvd/runner_defs.h
+++ b/host/commands/run_cvd/runner_defs.h
@@ -48,6 +48,7 @@
 
 // Actions supported by the launcher server
 enum class LauncherAction : char {
+  kStatus = 'I',
   kStop = 'X',
 };