Refactor starter_cmd a bit
This patch switches the order of arguments for starter_cmd. In
particular, the subcommand now comes first and then you specify the
name of the application.
For example:
$ starter_cmd start <app>
$ starter_cmd stop <app>
$ starter_cmd status <app>
This patch also enhances the "status" command to work without any
arguments. In this case, it will print out a high-level status for all
applications.
The error reporting on incorrect arguments to starter_cmd should also
be quite a bit improved with this change.
Change-Id: I4c80e6662b6f3d3e630a4be26d579e1166a89643
diff --git a/aos/starter/BUILD b/aos/starter/BUILD
index 8ad3dbf..de6e249 100644
--- a/aos/starter/BUILD
+++ b/aos/starter/BUILD
@@ -96,6 +96,7 @@
deps = [
":starter_rpc_lib",
"@com_github_google_glog//:glog",
+ "@com_google_absl//absl/strings:str_format",
],
)
diff --git a/aos/starter/starter_cmd.cc b/aos/starter/starter_cmd.cc
index d076afc..1381fa3 100644
--- a/aos/starter/starter_cmd.cc
+++ b/aos/starter/starter_cmd.cc
@@ -1,7 +1,9 @@
#include <chrono>
+#include <functional>
#include <iostream>
#include <unordered_map>
+#include "absl/strings/str_format.h"
#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
#include "gflags/gflags.h"
@@ -9,36 +11,53 @@
DEFINE_string(config, "./config.json", "File path of aos configuration");
-static const std::unordered_map<std::string, aos::starter::Command> kCommands{
- {"start", aos::starter::Command::START},
- {"stop", aos::starter::Command::STOP},
- {"restart", aos::starter::Command::RESTART}};
+namespace {
-int main(int argc, char **argv) {
- aos::InitGoogle(&argc, &argv);
+static const std::unordered_map<std::string, aos::starter::Command>
+ kCommandConversions{{"start", aos::starter::Command::START},
+ {"stop", aos::starter::Command::STOP},
+ {"restart", aos::starter::Command::RESTART}};
- CHECK(argc == 3) << "Invalid number of command arguments";
-
- const std::string application_name = argv[1];
- const std::string command_str = argv[2];
-
- aos::FlatbufferDetachedBuffer<aos::Configuration> config =
- aos::configuration::ReadConfig(FLAGS_config);
-
- if (command_str == "status") {
- auto status = aos::starter::GetStatus(application_name, &config.message());
+bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
+ if (argc == 1) {
+ // Print status for all processes.
+ auto status = aos::starter::GetStarterStatus(config);
+ for (const aos::starter::ApplicationStatus *app_status :
+ *status.message().statuses()) {
+ absl::PrintF("%-30s %s\n", app_status->name()->string_view(),
+ aos::starter::EnumNameState(app_status->state()));
+ }
+ } else if (argc == 2) {
+ // Print status for the specified process.
+ const char *application_name = argv[1];
+ auto status = aos::starter::GetStatus(application_name, config);
std::cout << aos::FlatbufferToJson(&status.message()) << '\n';
+ } else {
+ LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
+ return true;
+ }
+ return false;
+}
- return 0;
+bool InteractWithProgram(int argc, char **argv,
+ const aos::Configuration *config) {
+ const char *command_string = argv[0];
+
+ if (argc != 2) {
+ LOG(ERROR) << "The \"" << command_string
+ << "\" command requires an application name as an argument.";
+ return true;
}
- const auto command_search = kCommands.find(command_str);
- CHECK(command_search != kCommands.end())
- << "Invalid command \"" << command_str << "\"";
- const aos::starter::Command command = command_search->second;
+ const auto command_search = kCommandConversions.find(command_string);
+ CHECK(command_search != kCommandConversions.end())
+ << "Internal error: \"" << command_string
+ << "\" is not in kCommandConversions.";
- if (aos::starter::SendCommandBlocking(command, application_name,
- &config.message(),
+ const aos::starter::Command command = command_search->second;
+ const char *application_name = argv[1];
+
+ if (aos::starter::SendCommandBlocking(command, application_name, config,
std::chrono::seconds(3))) {
switch (command) {
case aos::starter::Command::START:
@@ -52,6 +71,57 @@
break;
}
} else {
- std::cout << "Failed to " << command_str << ' ' << application_name << '\n';
+ std::cout << "Failed to " << command_string << ' ' << application_name
+ << '\n';
}
+ return false;
+}
+
+// This is the set of subcommands we support. Each subcommand accepts argc and
+// argv from its own point of view. So argv[0] is always the name of the
+// subcommand. argv[1] and up are the arguments to the subcommand.
+// The subcommand returns true if there was an error parsing the command line
+// arguments. It returns false when the command line arguments are parsed
+// successfully.
+static const std::unordered_map<
+ std::string, std::function<bool(int argc, char **argv,
+ const aos::Configuration *config)>>
+ kCommands{
+ {"status", GetStarterStatus},
+ {"start", InteractWithProgram},
+ {"stop", InteractWithProgram},
+ {"restart", InteractWithProgram},
+ };
+
+} // namespace
+
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ bool parsing_failed = false;
+
+ if (argc < 2) {
+ parsing_failed = true;
+ } else {
+ const char *command = argv[1];
+ auto it = kCommands.find(command);
+ if (it == kCommands.end()) {
+ parsing_failed = true;
+ } else {
+ parsing_failed = it->second(argc - 1, argv + 1, &config.message());
+ }
+ }
+
+ if (parsing_failed) {
+ LOG(ERROR) << "Parsing failed. Valid commands are:";
+ for (auto entry: kCommands) {
+ LOG(ERROR) << " - " << entry.first;
+ }
+ return 1;
+ }
+
+ return 0;
}
diff --git a/aos/starter/starter_rpc_lib.cc b/aos/starter/starter_rpc_lib.cc
index efb4042..89fc2c2 100644
--- a/aos/starter/starter_rpc_lib.cc
+++ b/aos/starter/starter_rpc_lib.cc
@@ -127,5 +127,19 @@
aos::starter::ApplicationStatus>::Empty();
}
+const aos::FlatbufferVector<aos::starter::Status> GetStarterStatus(
+ const aos::Configuration *config) {
+ ShmEventLoop event_loop(config);
+ event_loop.SkipAosLog();
+
+ auto status_fetcher = event_loop.MakeFetcher<aos::starter::Status>("/aos");
+ status_fetcher.Fetch();
+ if (status_fetcher) {
+ return status_fetcher.CopyFlatBuffer();
+ } else {
+ return FlatbufferVector<aos::starter::Status>::Empty();
+ }
+}
+
} // namespace starter
} // namespace aos
diff --git a/aos/starter/starter_rpc_lib.h b/aos/starter/starter_rpc_lib.h
index 57c9e6b..152016a 100644
--- a/aos/starter/starter_rpc_lib.h
+++ b/aos/starter/starter_rpc_lib.h
@@ -30,6 +30,11 @@
const aos::FlatbufferDetachedBuffer<aos::starter::ApplicationStatus> GetStatus(
std::string_view name, const aos::Configuration *config);
+// Fetches the entire status message of starter. Creates a temporary event loop
+// from the provided config for fetching.
+const aos::FlatbufferVector<aos::starter::Status> GetStarterStatus(
+ const aos::Configuration *config);
+
} // namespace starter
} // namespace aos