blob: 8f9e1253b147c4c96db22ee6a63145c4cbefdf1d [file] [log] [blame]
Tyler Chatowa79419d2020-08-12 20:12:11 -07001#include <chrono>
Philipp Schrader08537492021-01-23 16:17:55 -08002#include <functional>
Tyler Chatowa79419d2020-08-12 20:12:11 -07003#include <iostream>
milind upadhyaya87957a2021-03-06 20:46:30 -08004#include <optional>
Tyler Chatowa79419d2020-08-12 20:12:11 -07005#include <unordered_map>
6
Philipp Schrader08537492021-01-23 16:17:55 -08007#include "absl/strings/str_format.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -07008#include "aos/init.h"
9#include "aos/json_to_flatbuffer.h"
milind upadhyaya87957a2021-03-06 20:46:30 -080010#include "aos/time/time.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070011#include "gflags/gflags.h"
12#include "starter_rpc_lib.h"
13
14DEFINE_string(config, "./config.json", "File path of aos configuration");
15
Philipp Schrader08537492021-01-23 16:17:55 -080016namespace {
Tyler Chatowa79419d2020-08-12 20:12:11 -070017
milind upadhyaya87957a2021-03-06 20:46:30 -080018namespace chrono = std::chrono;
19
Philipp Schrader08537492021-01-23 16:17:55 -080020static const std::unordered_map<std::string, aos::starter::Command>
21 kCommandConversions{{"start", aos::starter::Command::START},
22 {"stop", aos::starter::Command::STOP},
23 {"restart", aos::starter::Command::RESTART}};
Tyler Chatowa79419d2020-08-12 20:12:11 -070024
milind upadhyaya87957a2021-03-06 20:46:30 -080025void PrintKey() {
26 absl::PrintF("%-30s %-30s %s\n\n", "Name", "Time since last started", "State");
27}
28
29void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
30 const aos::monotonic_clock::time_point &time) {
31 const auto last_start_time =
32 aos::monotonic_clock::time_point(chrono::nanoseconds(app_status->last_start_time()));
33 const auto time_running =
34 chrono::duration_cast<chrono::seconds>(time - last_start_time);
35 absl::PrintF("%-30s %-30s %s\n", app_status->name()->string_view(),
36 std::to_string(time_running.count()) + 's',
37 aos::starter::EnumNameState(app_status->state()));
38}
39
Philipp Schrader08537492021-01-23 16:17:55 -080040bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
41 if (argc == 1) {
42 // Print status for all processes.
milind upadhyaya87957a2021-03-06 20:46:30 -080043 const auto optional_status = aos::starter::GetStarterStatus(config);
44 if (optional_status) {
45 auto status = *optional_status;
46 const auto time = aos::monotonic_clock::now();
47 PrintKey();
48 for (const aos::starter::ApplicationStatus *app_status :
49 *status.message().statuses()) {
50 PrintApplicationStatus(app_status, time);
51 }
52 } else {
53 LOG(WARNING) << "No status found";
Philipp Schrader08537492021-01-23 16:17:55 -080054 }
55 } else if (argc == 2) {
56 // Print status for the specified process.
57 const char *application_name = argv[1];
58 auto status = aos::starter::GetStatus(application_name, config);
milind upadhyaya87957a2021-03-06 20:46:30 -080059 PrintKey();
60 PrintApplicationStatus(&status.message(), aos::monotonic_clock::now());
Philipp Schrader08537492021-01-23 16:17:55 -080061 } else {
62 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
63 return true;
64 }
65 return false;
66}
Tyler Chatowa79419d2020-08-12 20:12:11 -070067
Philipp Schrader08537492021-01-23 16:17:55 -080068bool InteractWithProgram(int argc, char **argv,
69 const aos::Configuration *config) {
70 const char *command_string = argv[0];
71
72 if (argc != 2) {
73 LOG(ERROR) << "The \"" << command_string
74 << "\" command requires an application name as an argument.";
75 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -070076 }
77
Philipp Schrader08537492021-01-23 16:17:55 -080078 const auto command_search = kCommandConversions.find(command_string);
79 CHECK(command_search != kCommandConversions.end())
80 << "Internal error: \"" << command_string
81 << "\" is not in kCommandConversions.";
Tyler Chatowa79419d2020-08-12 20:12:11 -070082
Philipp Schrader08537492021-01-23 16:17:55 -080083 const aos::starter::Command command = command_search->second;
84 const char *application_name = argv[1];
85
86 if (aos::starter::SendCommandBlocking(command, application_name, config,
milind upadhyaya87957a2021-03-06 20:46:30 -080087 chrono::seconds(3))) {
Tyler Chatowa79419d2020-08-12 20:12:11 -070088 switch (command) {
89 case aos::starter::Command::START:
90 std::cout << "Successfully started " << application_name << '\n';
91 break;
92 case aos::starter::Command::STOP:
93 std::cout << "Successfully stopped " << application_name << '\n';
94 break;
95 case aos::starter::Command::RESTART:
96 std::cout << "Successfully restarted " << application_name << '\n';
97 break;
98 }
99 } else {
Philipp Schrader08537492021-01-23 16:17:55 -0800100 std::cout << "Failed to " << command_string << ' ' << application_name
101 << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700102 }
Philipp Schrader08537492021-01-23 16:17:55 -0800103 return false;
104}
105
106// This is the set of subcommands we support. Each subcommand accepts argc and
107// argv from its own point of view. So argv[0] is always the name of the
108// subcommand. argv[1] and up are the arguments to the subcommand.
109// The subcommand returns true if there was an error parsing the command line
110// arguments. It returns false when the command line arguments are parsed
111// successfully.
112static const std::unordered_map<
113 std::string, std::function<bool(int argc, char **argv,
114 const aos::Configuration *config)>>
115 kCommands{
116 {"status", GetStarterStatus},
117 {"start", InteractWithProgram},
118 {"stop", InteractWithProgram},
119 {"restart", InteractWithProgram},
120 };
121
122} // namespace
123
124int main(int argc, char **argv) {
125 aos::InitGoogle(&argc, &argv);
126
127 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
128 aos::configuration::ReadConfig(FLAGS_config);
129
130 bool parsing_failed = false;
131
132 if (argc < 2) {
133 parsing_failed = true;
134 } else {
135 const char *command = argv[1];
136 auto it = kCommands.find(command);
137 if (it == kCommands.end()) {
138 parsing_failed = true;
139 } else {
140 parsing_failed = it->second(argc - 1, argv + 1, &config.message());
141 }
142 }
143
144 if (parsing_failed) {
145 LOG(ERROR) << "Parsing failed. Valid commands are:";
146 for (auto entry: kCommands) {
147 LOG(ERROR) << " - " << entry.first;
148 }
149 return 1;
150 }
151
152 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700153}