blob: 9cf24368f26d95545fc09b3970475e2969e6374a [file] [log] [blame]
Brian J Griglak6605beb2022-05-23 17:31:57 -06001#include <algorithm>
Tyler Chatowa79419d2020-08-12 20:12:11 -07002#include <chrono>
Philipp Schrader08537492021-01-23 16:17:55 -08003#include <functional>
Tyler Chatowa79419d2020-08-12 20:12:11 -07004#include <iostream>
milind upadhyaya87957a2021-03-06 20:46:30 -08005#include <optional>
milind upadhyay4272f382021-04-07 18:03:08 -07006#include <string_view>
Tyler Chatowa79419d2020-08-12 20:12:11 -07007#include <unordered_map>
8
Philipp Schrader08537492021-01-23 16:17:55 -08009#include "absl/strings/str_format.h"
James Kuszmaul293b2172021-11-10 16:20:48 -080010#include "absl/strings/str_join.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070011#include "aos/init.h"
12#include "aos/json_to_flatbuffer.h"
milind upadhyaya87957a2021-03-06 20:46:30 -080013#include "aos/time/time.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070014#include "gflags/gflags.h"
15#include "starter_rpc_lib.h"
16
Austin Schuh8e2dfc62022-08-17 16:36:00 -070017DEFINE_string(config, "aos_config.json", "File path of aos configuration");
James Kuszmaul293b2172021-11-10 16:20:48 -080018// TODO(james): Bash autocompletion for node names.
19DEFINE_string(
20 node, "",
21 "Node to interact with. If empty, just interact with local node.");
22DEFINE_bool(all_nodes, false, "Interact with all nodes.");
Tyler Chatowa79419d2020-08-12 20:12:11 -070023
milind-u08dab882021-10-17 16:24:08 -070024DEFINE_bool(_bash_autocomplete, false,
25 "Internal use: Outputs commands or applications for use with "
26 "autocomplete script.");
27DEFINE_string(_bash_autocomplete_word, "",
28 "Internal use: Current word being autocompleted");
Brian J Griglak6605beb2022-05-23 17:31:57 -060029DEFINE_string(sort, "name",
30 "The name of the column to sort processes by. "
31 "Can be \"name\", \"state\", \"pid\", or \"uptime\".");
milind-u08dab882021-10-17 16:24:08 -070032
Philipp Schrader08537492021-01-23 16:17:55 -080033namespace {
Tyler Chatowa79419d2020-08-12 20:12:11 -070034
milind upadhyaya87957a2021-03-06 20:46:30 -080035namespace chrono = std::chrono;
36
Philipp Schrader08537492021-01-23 16:17:55 -080037static const std::unordered_map<std::string, aos::starter::Command>
38 kCommandConversions{{"start", aos::starter::Command::START},
39 {"stop", aos::starter::Command::STOP},
40 {"restart", aos::starter::Command::RESTART}};
Tyler Chatowa79419d2020-08-12 20:12:11 -070041
James Kuszmaul293b2172021-11-10 16:20:48 -080042std::vector<const aos::Node *> InteractNodes(
43 const aos::Configuration *configuration) {
Austin Schuh43fceaf2021-10-16 14:20:22 -070044 if (!configuration->has_nodes()) {
James Kuszmaul293b2172021-11-10 16:20:48 -080045 return {nullptr};
Austin Schuh43fceaf2021-10-16 14:20:22 -070046 }
47
James Kuszmaul293b2172021-11-10 16:20:48 -080048 if (!FLAGS_node.empty()) {
49 CHECK(!FLAGS_all_nodes) << "Can't specify both --node and --all_nodes.";
50 return {aos::configuration::GetNode(configuration, FLAGS_node)};
51 }
52
53 if (FLAGS_all_nodes) {
54 return aos::configuration::GetNodes(configuration);
55 }
56
57 return {aos::configuration::GetMyNode(configuration)};
Austin Schuh43fceaf2021-10-16 14:20:22 -070058}
59
James Kuszmaul293b2172021-11-10 16:20:48 -080060std::vector<const aos::Node *> InteractNodesForApplication(
61 const aos::Configuration *config, std::string_view application_name) {
62 const std::vector<const aos::Node *> interact_nodes = InteractNodes(config);
63 std::vector<const aos::Node *> application_nodes;
64 std::vector<std::string> debug_node_names;
65 for (const aos::Node *node : interact_nodes) {
66 if (aos::configuration::GetApplication(config, node, application_name) !=
67 nullptr) {
68 application_nodes.push_back(node);
Austin Schuh43fceaf2021-10-16 14:20:22 -070069 }
James Kuszmaul293b2172021-11-10 16:20:48 -080070 if (node != nullptr) {
71 debug_node_names.push_back(node->name()->str());
72 }
Austin Schuh43fceaf2021-10-16 14:20:22 -070073 }
James Kuszmaul293b2172021-11-10 16:20:48 -080074
75 if (application_nodes.empty()) {
76 if (interact_nodes.size() == 1 && interact_nodes[0] == nullptr) {
77 std::cout << "Unknown application " << application_name << std::endl;
78 } else {
79 std::cout << "Unknown application " << application_name
80 << " on any of node(s) "
81 << absl::StrJoin(debug_node_names, ", ") << std::endl;
82 }
83 }
84 return application_nodes;
Austin Schuh43fceaf2021-10-16 14:20:22 -070085}
86
milind upadhyaya87957a2021-03-06 20:46:30 -080087void PrintKey() {
James Kuszmaulce3497e2023-02-26 12:40:26 -080088 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n", "Name", "Node", "State",
89 "PID", "Uptime", "Last Exit Code");
milind upadhyaya87957a2021-03-06 20:46:30 -080090}
91
Brian J Griglak6605beb2022-05-23 17:31:57 -060092std::vector<const aos::starter::ApplicationStatus *> SortApplications(
93 const aos::FlatbufferVector<aos::starter::Status> &status) {
94 std::vector<const aos::starter::ApplicationStatus *> sorted_statuses;
95 for (const aos::starter::ApplicationStatus *app_status :
96 *status.message().statuses()) {
97 sorted_statuses.push_back(app_status);
98 }
99 // If --sort flag not set, then return this unsorted vector as is.
100 if (FLAGS_sort.empty()) {
101 return sorted_statuses;
102 }
103
104 // Convert --sort flag to lowercase for testing below.
105 std::transform(FLAGS_sort.begin(), FLAGS_sort.end(), FLAGS_sort.begin(),
106 tolower);
107
108 // This function is called once for each node being reported upon, so there is
109 // no need to sort on node, it happens implicitly.
110
111 if (FLAGS_sort == "name") {
112 // Sort on name using std::string_view::operator< for lexicographic order.
113 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
114 [](const aos::starter::ApplicationStatus *lhs,
115 const aos::starter::ApplicationStatus *rhs) {
116 return lhs->name()->string_view() < rhs->name()->string_view();
117 });
118 } else if (FLAGS_sort == "state") {
119 // Sort on state first, and then name for apps in same state.
120 // ApplicationStatus::state is an enum, so need to call EnumNameState()
121 // convenience wrapper to convert enum to char*, and then wrap in
122 // std::string_view for lexicographic ordering.
123 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
124 [](const aos::starter::ApplicationStatus *lhs,
125 const aos::starter::ApplicationStatus *rhs) {
126 return (lhs->state() != rhs->state())
127 ? (std::string_view(
128 aos::starter::EnumNameState(lhs->state())) <
129 std::string_view(
130 aos::starter::EnumNameState(rhs->state())))
131 : (lhs->name()->string_view() <
132 rhs->name()->string_view());
133 });
134 } else if (FLAGS_sort == "pid") {
135 // Sort on pid first, and then name for when both apps are not running.
136 // If the app state is STOPPED, then it will not have a pid, so need to test
137 // that first. If only one app is STOPPED, then return Boolean state to put
138 // running apps before stopped.
139 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
140 [](const aos::starter::ApplicationStatus *lhs,
141 const aos::starter::ApplicationStatus *rhs) {
142 if (lhs->state() == aos::starter::State::STOPPED) {
143 if (rhs->state() == aos::starter::State::STOPPED) {
144 return lhs->name()->string_view() <
145 rhs->name()->string_view();
146 } else {
147 return false;
148 }
149 } else {
150 if (rhs->state() == aos::starter::State::STOPPED) {
151 return true;
152 } else {
153 return lhs->pid() < rhs->pid();
154 }
155 }
156 });
157 } else if (FLAGS_sort == "uptime") {
158 // Sort on last_start_time first, and then name for when both apps are not
159 // running, or have exact same start time. Only use last_start_time when app
160 // is not STOPPED. If only one app is STOPPED, then return Boolean state to
161 // put running apps before stopped.
162 std::sort(
163 sorted_statuses.begin(), sorted_statuses.end(),
164 [](const aos::starter::ApplicationStatus *lhs,
165 const aos::starter::ApplicationStatus *rhs) {
166 if (lhs->state() == aos::starter::State::STOPPED) {
167 if (rhs->state() == aos::starter::State::STOPPED) {
168 return lhs->name()->string_view() < rhs->name()->string_view();
169 } else {
170 return false;
171 }
172 } else {
173 if (rhs->state() == aos::starter::State::STOPPED) {
174 return true;
175 } else {
176 return (lhs->last_start_time() == rhs->last_start_time())
177 ? (lhs->name()->string_view() <
178 rhs->name()->string_view())
179 : (lhs->last_start_time() < rhs->last_start_time());
180 }
181 }
182 });
183 } else {
184 std::cerr << "Unknown sort criteria \"" << FLAGS_sort << "\"" << std::endl;
185 exit(1);
186 }
187
188 return sorted_statuses;
189}
190
milind upadhyaya87957a2021-03-06 20:46:30 -0800191void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
James Kuszmaul293b2172021-11-10 16:20:48 -0800192 const aos::monotonic_clock::time_point &time,
193 const aos::Node *node) {
milind upadhyay4272f382021-04-07 18:03:08 -0700194 const auto last_start_time = aos::monotonic_clock::time_point(
195 chrono::nanoseconds(app_status->last_start_time()));
milind upadhyaya87957a2021-03-06 20:46:30 -0800196 const auto time_running =
197 chrono::duration_cast<chrono::seconds>(time - last_start_time);
James Kuszmaulce3497e2023-02-26 12:40:26 -0800198 const std::string last_exit_code =
199 app_status->has_last_exit_code()
200 ? std::to_string(app_status->last_exit_code())
201 : "-";
Austin Schuhf4334002021-10-16 14:19:51 -0700202 if (app_status->state() == aos::starter::State::STOPPED) {
James Kuszmaulce3497e2023-02-26 12:40:26 -0800203 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n",
James Kuszmaul293b2172021-11-10 16:20:48 -0800204 app_status->name()->string_view(),
205 (node == nullptr) ? "none" : node->name()->string_view(),
James Kuszmaulce3497e2023-02-26 12:40:26 -0800206 aos::starter::EnumNameState(app_status->state()), "", "",
207 last_exit_code);
208 } else {
209 absl::PrintF(
210 "%-30s %-10s %-8s %-6d %-9s %-13s\n", app_status->name()->string_view(),
211 (node == nullptr) ? "none" : node->name()->string_view(),
212 aos::starter::EnumNameState(app_status->state()), app_status->pid(),
213 std::to_string(time_running.count()) + 's', last_exit_code);
Austin Schuhf4334002021-10-16 14:19:51 -0700214 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800215}
216
Austin Schuh43fceaf2021-10-16 14:20:22 -0700217// Prints the status for all applications.
218void GetAllStarterStatus(const aos::Configuration *config) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800219 PrintKey();
220 std::vector<const aos::Node *> missing_nodes;
221 for (const aos::Node *node : InteractNodes(config)) {
222 // Print status for all processes.
223 const auto optional_status = aos::starter::GetStarterStatus(config, node);
224 if (optional_status) {
225 const aos::FlatbufferVector<aos::starter::Status> &status =
226 optional_status->second;
227 const aos::monotonic_clock::time_point time = optional_status->first;
Brian J Griglak6605beb2022-05-23 17:31:57 -0600228 const auto &sorted_statuses = SortApplications(status);
James Kuszmaul293b2172021-11-10 16:20:48 -0800229 for (const aos::starter::ApplicationStatus *app_status :
Brian J Griglak6605beb2022-05-23 17:31:57 -0600230 sorted_statuses) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800231 PrintApplicationStatus(app_status, time, node);
232 }
233 } else {
234 missing_nodes.push_back(node);
Philipp Schrader08537492021-01-23 16:17:55 -0800235 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800236 }
237 for (const aos::Node *node : missing_nodes) {
238 if (node == nullptr) {
239 LOG(WARNING) << "No status found.";
240 } else {
241 LOG(WARNING) << "No status found for node "
242 << node->name()->string_view();
243 }
milind-u08dab882021-10-17 16:24:08 -0700244 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700245}
246
247// Handles the "status" command. Returns true if the help message should be
248// printed.
249bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
250 if (argc == 1) {
251 GetAllStarterStatus(config);
Philipp Schrader08537492021-01-23 16:17:55 -0800252 } else if (argc == 2) {
253 // Print status for the specified process.
milind upadhyay4272f382021-04-07 18:03:08 -0700254 const auto application_name =
255 aos::starter::FindApplication(argv[1], config);
Austin Schuh43fceaf2021-10-16 14:20:22 -0700256 if (application_name == "all") {
257 GetAllStarterStatus(config);
258 return false;
259 }
260
James Kuszmaul293b2172021-11-10 16:20:48 -0800261 const std::vector<const aos::Node *> application_nodes =
262 InteractNodesForApplication(config, application_name);
263 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700264 return false;
265 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800266 PrintKey();
James Kuszmaul293b2172021-11-10 16:20:48 -0800267 for (const aos::Node *node : application_nodes) {
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800268 auto optional_status =
269 aos::starter::GetStatus(application_name, config, node);
270 if (optional_status.has_value()) {
James Kuszmaul2ca441b2022-01-07 18:16:23 -0800271 PrintApplicationStatus(&optional_status.value().second.message(),
272 optional_status.value().first, node);
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800273 } else {
274 if (node != nullptr) {
275 LOG(ERROR) << "No status available yet for \"" << application_name
276 << "\" on node \"" << node->name()->string_view() << "\".";
277 } else {
278 LOG(ERROR) << "No status available yet for \"" << application_name
279 << "\".";
280 }
281 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800282 }
Philipp Schrader08537492021-01-23 16:17:55 -0800283 } else {
284 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
285 return true;
286 }
287 return false;
288}
Tyler Chatowa79419d2020-08-12 20:12:11 -0700289
Austin Schuh43fceaf2021-10-16 14:20:22 -0700290// Sends the provided command to all applications. Prints the success text on
291// success, and failure text on failure.
292void InteractWithAll(const aos::Configuration *config,
293 const aos::starter::Command command,
294 std::string_view success_text,
295 std::string_view failure_text) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800296 std::map<const aos::Node *,
297 std::unique_ptr<aos::FlatbufferVector<aos::starter::Status>>>
298 statuses;
299
300 for (const aos::Node *node : InteractNodes(config)) {
301 std::optional<std::pair<aos::monotonic_clock::time_point,
302 const aos::FlatbufferVector<aos::starter::Status>>>
303 optional_status = aos::starter::GetStarterStatus(config, node);
304 if (optional_status.has_value()) {
305 statuses[node] =
306 std::make_unique<aos::FlatbufferVector<aos::starter::Status>>(
307 optional_status.value().second);
308 } else {
309 if (node == nullptr) {
310 LOG(WARNING) << "Starter not running";
311 } else {
312 LOG(WARNING) << "Starter not running on node "
313 << node->name()->string_view();
314 }
315 }
316 }
317
318 if (!statuses.empty()) {
319 std::vector<aos::starter::ApplicationCommand> commands;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700320
321 for (const aos::Application *application : *config->applications()) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800322 const std::string_view application_name =
323 application->name()->string_view();
324 const std::vector<const aos::Node *> application_nodes =
325 InteractNodesForApplication(config, application_name);
326 // Ignore any applications which aren't supposed to be started.
327 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700328 continue;
329 }
330
James Kuszmaul293b2172021-11-10 16:20:48 -0800331 std::vector<const aos::Node *> running_nodes;
332 if (application->autostart()) {
333 running_nodes = application_nodes;
334 } else {
335 for (const aos::Node *node : application_nodes) {
336 const aos::starter::ApplicationStatus *application_status =
337 aos::starter::FindApplicationStatus(statuses[node]->message(),
338 application_name);
339 if (application_status->state() == aos::starter::State::STOPPED) {
340 if (node == nullptr) {
341 std::cout << "Skipping " << application_name
342 << " because it is STOPPED\n";
343 } else {
344 std::cout << "Skipping " << application_name << " on "
345 << node->name()->string_view()
346 << " because it is STOPPED\n";
347 }
348 continue;
349 } else {
350 running_nodes.push_back(node);
351 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700352 }
353 }
354
James Kuszmaul293b2172021-11-10 16:20:48 -0800355 if (!running_nodes.empty()) {
356 commands.emplace_back(aos::starter::ApplicationCommand{
357 command, application_name, running_nodes});
358 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700359 }
360
361 // Restart each running process
362 if (aos::starter::SendCommandBlocking(commands, config,
363 chrono::seconds(5))) {
364 std::cout << success_text << "all \n";
365 } else {
366 std::cout << failure_text << "all \n";
367 }
368 } else {
James Kuszmaul293b2172021-11-10 16:20:48 -0800369 LOG(WARNING) << "None of the starters we care about are running.";
Austin Schuh43fceaf2021-10-16 14:20:22 -0700370 }
371}
372
373// Handles the "start", "stop", and "restart" commands. Returns true if the
374// help message should be printed.
Philipp Schrader08537492021-01-23 16:17:55 -0800375bool InteractWithProgram(int argc, char **argv,
376 const aos::Configuration *config) {
377 const char *command_string = argv[0];
Philipp Schrader08537492021-01-23 16:17:55 -0800378 if (argc != 2) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700379 LOG(ERROR)
380 << "The \"" << command_string
381 << "\" command requires an application name or 'all' as an argument.";
Philipp Schrader08537492021-01-23 16:17:55 -0800382 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700383 }
384
Philipp Schrader08537492021-01-23 16:17:55 -0800385 const auto command_search = kCommandConversions.find(command_string);
386 CHECK(command_search != kCommandConversions.end())
387 << "Internal error: \"" << command_string
388 << "\" is not in kCommandConversions.";
Philipp Schrader08537492021-01-23 16:17:55 -0800389 const aos::starter::Command command = command_search->second;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700390
391 std::string_view success_text;
392 const std::string failure_text =
393 std::string("Failed to ") + std::string(command_string) + " ";
394 switch (command) {
395 case aos::starter::Command::START:
396 success_text = "Successfully started ";
397 break;
398 case aos::starter::Command::STOP:
399 success_text = "Successfully stopped ";
400 break;
401 case aos::starter::Command::RESTART:
402 success_text = "Successfully restarted ";
403 break;
404 }
405
406 const std::string_view application_name =
407 aos::starter::FindApplication(argv[1], config);
408 if (application_name == "all") {
409 InteractWithAll(config, command, success_text, failure_text);
410 return false;
411 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800412
413 const std::vector<const aos::Node *> application_nodes =
414 InteractNodesForApplication(config, application_name);
415 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700416 return false;
417 }
418
Philipp Schrader08537492021-01-23 16:17:55 -0800419 if (aos::starter::SendCommandBlocking(command, application_name, config,
James Kuszmaul293b2172021-11-10 16:20:48 -0800420 chrono::seconds(5),
421 application_nodes)) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700422 std::cout << success_text << application_name << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700423 } else {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700424 std::cout << failure_text << application_name << '\n';
Jacob Ismael6388db92021-06-28 22:51:24 -0700425 }
426 return false;
427}
428
Austin Schuh33cc4162021-10-16 14:20:28 -0700429bool Help(int /*argc*/, char ** /*argv*/,
430 const aos::Configuration * /*config*/);
431
Philipp Schrader08537492021-01-23 16:17:55 -0800432// This is the set of subcommands we support. Each subcommand accepts argc and
433// argv from its own point of view. So argv[0] is always the name of the
434// subcommand. argv[1] and up are the arguments to the subcommand.
435// The subcommand returns true if there was an error parsing the command line
436// arguments. It returns false when the command line arguments are parsed
437// successfully.
Austin Schuh33cc4162021-10-16 14:20:28 -0700438static const std::vector<
439 std::tuple<std::string,
440 std::function<bool(int argc, char **argv,
441 const aos::Configuration *config)>,
442 std::string_view>>
443 kCommands{
444 {"help", Help, ""},
445 {"status", GetStarterStatus,
446 " [application], Returns the status of the provided application, "
447 "or all applications by default"},
448 {"start", InteractWithProgram,
449 " application, Starts the provided application, "
450 "or all applications if all is provided"},
451 {"stop", InteractWithProgram,
452 " application, Stops the provided application, "
453 "or all applications if all is provided"},
454 {"restart", InteractWithProgram,
455 " application, Restarts the provided application, "
456 "or all applications if all is provided"}};
457
458bool Help(int /*argc*/, char ** /*argv*/,
459 const aos::Configuration * /*config*/) {
460 std::cout << "Valid commands are:" << std::endl;
461 for (auto entry : kCommands) {
462 std::cout << " - " << std::get<0>(entry) << std::get<2>(entry) << std::endl;
463 }
464 return false;
465}
Philipp Schrader08537492021-01-23 16:17:55 -0800466
milind-u08dab882021-10-17 16:24:08 -0700467void Autocomplete(int argc, char **argv, const aos::Configuration *config) {
468 const std::string_view command = (argc >= 2 ? argv[1] : "");
469 const std::string_view app_name = (argc >= 3 ? argv[2] : "");
470
471 std::cout << "COMPREPLY=(";
472 if (FLAGS__bash_autocomplete_word == command) {
473 // Autocomplete the starter command
474 for (const auto &entry : kCommands) {
475 if (std::get<0>(entry).find(command) == 0) {
476 std::cout << '\'' << std::get<0>(entry) << "' ";
477 }
478 }
479 } else {
480 // Autocomplete the app name
481 for (const auto *app : *config->applications()) {
482 if (app->has_name() && app->name()->string_view().find(app_name) == 0) {
483 std::cout << '\'' << app->name()->string_view() << "' ";
484 }
485 }
486
487 // Autocomplete with "all"
milind-u95296dd2021-10-19 07:42:17 -0700488 if (std::string_view("all").find(app_name) == 0) {
milind-u08dab882021-10-17 16:24:08 -0700489 std::cout << "'all'";
490 }
491 }
492 std::cout << ')';
493}
494
Philipp Schrader08537492021-01-23 16:17:55 -0800495} // namespace
496
497int main(int argc, char **argv) {
498 aos::InitGoogle(&argc, &argv);
499
500 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
501 aos::configuration::ReadConfig(FLAGS_config);
502
milind-u08dab882021-10-17 16:24:08 -0700503 if (FLAGS__bash_autocomplete) {
504 Autocomplete(argc, argv, &config.message());
505 return 0;
506 }
507
Philipp Schrader08537492021-01-23 16:17:55 -0800508 bool parsing_failed = false;
509
510 if (argc < 2) {
511 parsing_failed = true;
512 } else {
513 const char *command = argv[1];
Austin Schuh33cc4162021-10-16 14:20:28 -0700514 auto it = std::find_if(
515 kCommands.begin(), kCommands.end(),
516 [command](const std::tuple<
517 std::string,
518 std::function<bool(int argc, char **argv,
519 const aos::Configuration *config)>,
520 std::string_view> &t) { return std::get<0>(t) == command; });
521
Philipp Schrader08537492021-01-23 16:17:55 -0800522 if (it == kCommands.end()) {
523 parsing_failed = true;
524 } else {
Austin Schuh33cc4162021-10-16 14:20:28 -0700525 parsing_failed = std::get<1>(*it)(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800526 }
527 }
528
529 if (parsing_failed) {
Austin Schuh33cc4162021-10-16 14:20:28 -0700530 Help(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800531 return 1;
532 }
533
534 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700535}