blob: 877b0aff019b605e61db8727d2d94f060877b48e [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>
milind upadhyay4272f382021-04-07 18:03:08 -07005#include <string_view>
Tyler Chatowa79419d2020-08-12 20:12:11 -07006#include <unordered_map>
7
Philipp Schrader08537492021-01-23 16:17:55 -08008#include "absl/strings/str_format.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -07009#include "aos/init.h"
10#include "aos/json_to_flatbuffer.h"
milind upadhyaya87957a2021-03-06 20:46:30 -080011#include "aos/time/time.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070012#include "gflags/gflags.h"
13#include "starter_rpc_lib.h"
14
15DEFINE_string(config, "./config.json", "File path of aos configuration");
16
Philipp Schrader08537492021-01-23 16:17:55 -080017namespace {
Tyler Chatowa79419d2020-08-12 20:12:11 -070018
milind upadhyaya87957a2021-03-06 20:46:30 -080019namespace chrono = std::chrono;
20
Philipp Schrader08537492021-01-23 16:17:55 -080021static const std::unordered_map<std::string, aos::starter::Command>
22 kCommandConversions{{"start", aos::starter::Command::START},
23 {"stop", aos::starter::Command::STOP},
24 {"restart", aos::starter::Command::RESTART}};
Tyler Chatowa79419d2020-08-12 20:12:11 -070025
Austin Schuh43fceaf2021-10-16 14:20:22 -070026const aos::Node *MaybeMyNode(const aos::Configuration *configuration) {
27 if (!configuration->has_nodes()) {
28 return nullptr;
29 }
30
31 return aos::configuration::GetMyNode(configuration);
32}
33
34bool ValidApplication(const aos::Configuration *config,
35 std::string_view application_name) {
36 const aos::Node *node = MaybeMyNode(config);
37 const aos::Application *application =
38 aos::configuration::GetApplication(config, node, application_name);
39 if (application == nullptr) {
40 if (node) {
41 std::cout << "Unknown application '" << application_name << "' on node '"
42 << node->name()->string_view() << "'" << std::endl;
43 } else {
44 std::cout << "Unknown application '" << application_name << "'"
45 << std::endl;
46 }
47 return false;
48 }
49 return true;
50}
51
milind upadhyaya87957a2021-03-06 20:46:30 -080052void PrintKey() {
Austin Schuhf4334002021-10-16 14:19:51 -070053 absl::PrintF("%-30s %-8s %-6s %-9s\n", "Name", "State", "PID", "Uptime");
milind upadhyaya87957a2021-03-06 20:46:30 -080054}
55
56void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
milind upadhyay4272f382021-04-07 18:03:08 -070057 const aos::monotonic_clock::time_point &time) {
58 const auto last_start_time = aos::monotonic_clock::time_point(
59 chrono::nanoseconds(app_status->last_start_time()));
milind upadhyaya87957a2021-03-06 20:46:30 -080060 const auto time_running =
61 chrono::duration_cast<chrono::seconds>(time - last_start_time);
Austin Schuhf4334002021-10-16 14:19:51 -070062 if (app_status->state() == aos::starter::State::STOPPED) {
63 absl::PrintF("%-30s %-8s\n", app_status->name()->string_view(),
64 aos::starter::EnumNameState(app_status->state()));
65 } else {
Austin Schuh31bbdea2021-10-16 15:51:37 -070066 absl::PrintF("%-30s %-8s %-6d %-9s\n", app_status->name()->string_view(),
Austin Schuhf4334002021-10-16 14:19:51 -070067 aos::starter::EnumNameState(app_status->state()),
Austin Schuh31bbdea2021-10-16 15:51:37 -070068 app_status->pid(), std::to_string(time_running.count()) + 's');
Austin Schuhf4334002021-10-16 14:19:51 -070069 }
milind upadhyaya87957a2021-03-06 20:46:30 -080070}
71
Austin Schuh43fceaf2021-10-16 14:20:22 -070072// Prints the status for all applications.
73void GetAllStarterStatus(const aos::Configuration *config) {
Philipp Schrader08537492021-01-23 16:17:55 -080074 // Print status for all processes.
milind upadhyaya87957a2021-03-06 20:46:30 -080075 const auto optional_status = aos::starter::GetStarterStatus(config);
76 if (optional_status) {
77 auto status = *optional_status;
78 const auto time = aos::monotonic_clock::now();
79 PrintKey();
80 for (const aos::starter::ApplicationStatus *app_status :
81 *status.message().statuses()) {
82 PrintApplicationStatus(app_status, time);
83 }
84 } else {
85 LOG(WARNING) << "No status found";
Philipp Schrader08537492021-01-23 16:17:55 -080086 }
Austin Schuh43fceaf2021-10-16 14:20:22 -070087}
88
89// Handles the "status" command. Returns true if the help message should be
90// printed.
91bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
92 if (argc == 1) {
93 GetAllStarterStatus(config);
Philipp Schrader08537492021-01-23 16:17:55 -080094 } else if (argc == 2) {
95 // Print status for the specified process.
milind upadhyay4272f382021-04-07 18:03:08 -070096 const auto application_name =
97 aos::starter::FindApplication(argv[1], config);
Austin Schuh43fceaf2021-10-16 14:20:22 -070098 if (application_name == "all") {
99 GetAllStarterStatus(config);
100 return false;
101 }
102
103 if (!ValidApplication(config, application_name)) {
104 return false;
105 }
Philipp Schrader08537492021-01-23 16:17:55 -0800106 auto status = aos::starter::GetStatus(application_name, config);
milind upadhyaya87957a2021-03-06 20:46:30 -0800107 PrintKey();
108 PrintApplicationStatus(&status.message(), aos::monotonic_clock::now());
Philipp Schrader08537492021-01-23 16:17:55 -0800109 } else {
110 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
111 return true;
112 }
113 return false;
114}
Tyler Chatowa79419d2020-08-12 20:12:11 -0700115
Austin Schuh43fceaf2021-10-16 14:20:22 -0700116// Sends the provided command to all applications. Prints the success text on
117// success, and failure text on failure.
118void InteractWithAll(const aos::Configuration *config,
119 const aos::starter::Command command,
120 std::string_view success_text,
121 std::string_view failure_text) {
122 const auto optional_status = aos::starter::GetStarterStatus(config);
123 if (optional_status) {
124 auto status = *optional_status;
125 const aos::Node *my_node = MaybeMyNode(config);
126 std::vector<std::pair<aos::starter::Command, std::string_view>> commands;
127
128 for (const aos::Application *application : *config->applications()) {
129 // Ignore any applications which aren't supposed to be started on this
130 // node.
131 if (!aos::configuration::ApplicationShouldStart(config, my_node,
132 application)) {
133 continue;
134 }
135
136 const std::string_view application_name =
137 application->name()->string_view();
138 if (!application->autostart()) {
139 const aos::starter::ApplicationStatus *application_status =
140 aos::starter::FindApplicationStatus(status.message(),
141 application_name);
142 if (application_status->state() == aos::starter::State::STOPPED) {
143 std::cout << "Skipping " << application_name
144 << " because it is STOPPED\n";
145 continue;
146 }
147 }
148
149 commands.emplace_back(command, application_name);
150 }
151
152 // Restart each running process
153 if (aos::starter::SendCommandBlocking(commands, config,
154 chrono::seconds(5))) {
155 std::cout << success_text << "all \n";
156 } else {
157 std::cout << failure_text << "all \n";
158 }
159 } else {
160 LOG(WARNING) << "Starter not running";
161 }
162}
163
164// Handles the "start", "stop", and "restart" commands. Returns true if the
165// help message should be printed.
Philipp Schrader08537492021-01-23 16:17:55 -0800166bool InteractWithProgram(int argc, char **argv,
167 const aos::Configuration *config) {
168 const char *command_string = argv[0];
Philipp Schrader08537492021-01-23 16:17:55 -0800169 if (argc != 2) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700170 LOG(ERROR)
171 << "The \"" << command_string
172 << "\" command requires an application name or 'all' as an argument.";
Philipp Schrader08537492021-01-23 16:17:55 -0800173 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700174 }
175
Philipp Schrader08537492021-01-23 16:17:55 -0800176 const auto command_search = kCommandConversions.find(command_string);
177 CHECK(command_search != kCommandConversions.end())
178 << "Internal error: \"" << command_string
179 << "\" is not in kCommandConversions.";
Philipp Schrader08537492021-01-23 16:17:55 -0800180 const aos::starter::Command command = command_search->second;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700181
182 std::string_view success_text;
183 const std::string failure_text =
184 std::string("Failed to ") + std::string(command_string) + " ";
185 switch (command) {
186 case aos::starter::Command::START:
187 success_text = "Successfully started ";
188 break;
189 case aos::starter::Command::STOP:
190 success_text = "Successfully stopped ";
191 break;
192 case aos::starter::Command::RESTART:
193 success_text = "Successfully restarted ";
194 break;
195 }
196
197 const std::string_view application_name =
198 aos::starter::FindApplication(argv[1], config);
199 if (application_name == "all") {
200 InteractWithAll(config, command, success_text, failure_text);
201 return false;
202 }
203 if (!ValidApplication(config, application_name)) {
204 return false;
205 }
206
Philipp Schrader08537492021-01-23 16:17:55 -0800207 if (aos::starter::SendCommandBlocking(command, application_name, config,
Austin Schuha07b3ce2021-10-10 12:33:21 -0700208 chrono::seconds(5))) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700209 std::cout << success_text << application_name << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700210 } else {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700211 std::cout << failure_text << application_name << '\n';
Jacob Ismael6388db92021-06-28 22:51:24 -0700212 }
213 return false;
214}
215
Austin Schuh33cc4162021-10-16 14:20:28 -0700216bool Help(int /*argc*/, char ** /*argv*/,
217 const aos::Configuration * /*config*/);
218
Philipp Schrader08537492021-01-23 16:17:55 -0800219// This is the set of subcommands we support. Each subcommand accepts argc and
220// argv from its own point of view. So argv[0] is always the name of the
221// subcommand. argv[1] and up are the arguments to the subcommand.
222// The subcommand returns true if there was an error parsing the command line
223// arguments. It returns false when the command line arguments are parsed
224// successfully.
Austin Schuh33cc4162021-10-16 14:20:28 -0700225static const std::vector<
226 std::tuple<std::string,
227 std::function<bool(int argc, char **argv,
228 const aos::Configuration *config)>,
229 std::string_view>>
230 kCommands{
231 {"help", Help, ""},
232 {"status", GetStarterStatus,
233 " [application], Returns the status of the provided application, "
234 "or all applications by default"},
235 {"start", InteractWithProgram,
236 " application, Starts the provided application, "
237 "or all applications if all is provided"},
238 {"stop", InteractWithProgram,
239 " application, Stops the provided application, "
240 "or all applications if all is provided"},
241 {"restart", InteractWithProgram,
242 " application, Restarts the provided application, "
243 "or all applications if all is provided"}};
244
245bool Help(int /*argc*/, char ** /*argv*/,
246 const aos::Configuration * /*config*/) {
247 std::cout << "Valid commands are:" << std::endl;
248 for (auto entry : kCommands) {
249 std::cout << " - " << std::get<0>(entry) << std::get<2>(entry) << std::endl;
250 }
251 return false;
252}
Philipp Schrader08537492021-01-23 16:17:55 -0800253
254} // namespace
255
256int main(int argc, char **argv) {
257 aos::InitGoogle(&argc, &argv);
258
259 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
260 aos::configuration::ReadConfig(FLAGS_config);
261
262 bool parsing_failed = false;
263
264 if (argc < 2) {
265 parsing_failed = true;
266 } else {
267 const char *command = argv[1];
Austin Schuh33cc4162021-10-16 14:20:28 -0700268 auto it = std::find_if(
269 kCommands.begin(), kCommands.end(),
270 [command](const std::tuple<
271 std::string,
272 std::function<bool(int argc, char **argv,
273 const aos::Configuration *config)>,
274 std::string_view> &t) { return std::get<0>(t) == command; });
275
Philipp Schrader08537492021-01-23 16:17:55 -0800276 if (it == kCommands.end()) {
277 parsing_failed = true;
278 } else {
Austin Schuh33cc4162021-10-16 14:20:28 -0700279 parsing_failed = std::get<1>(*it)(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800280 }
281 }
282
283 if (parsing_failed) {
Austin Schuh33cc4162021-10-16 14:20:28 -0700284 Help(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800285 return 1;
286 }
287
288 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700289}