blob: 6c172febe893c4183f96abac788d199a07017c81 [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"
Philipp Schrader790cb542023-07-05 21:06:52 -070011#include "gflags/gflags.h"
12
Tyler Chatowa79419d2020-08-12 20:12:11 -070013#include "aos/init.h"
14#include "aos/json_to_flatbuffer.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070015#include "aos/starter/starter_rpc_lib.h"
milind upadhyaya87957a2021-03-06 20:46:30 -080016#include "aos/time/time.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070017
Austin Schuh8e2dfc62022-08-17 16:36:00 -070018DEFINE_string(config, "aos_config.json", "File path of aos configuration");
James Kuszmaul293b2172021-11-10 16:20:48 -080019// TODO(james): Bash autocompletion for node names.
20DEFINE_string(
21 node, "",
22 "Node to interact with. If empty, just interact with local node.");
23DEFINE_bool(all_nodes, false, "Interact with all nodes.");
Tyler Chatowa79419d2020-08-12 20:12:11 -070024
milind-u08dab882021-10-17 16:24:08 -070025DEFINE_bool(_bash_autocomplete, false,
26 "Internal use: Outputs commands or applications for use with "
27 "autocomplete script.");
28DEFINE_string(_bash_autocomplete_word, "",
29 "Internal use: Current word being autocompleted");
Brian J Griglak6605beb2022-05-23 17:31:57 -060030DEFINE_string(sort, "name",
31 "The name of the column to sort processes by. "
32 "Can be \"name\", \"state\", \"pid\", or \"uptime\".");
milind-u08dab882021-10-17 16:24:08 -070033
Philipp Schrader08537492021-01-23 16:17:55 -080034namespace {
Tyler Chatowa79419d2020-08-12 20:12:11 -070035
milind upadhyaya87957a2021-03-06 20:46:30 -080036namespace chrono = std::chrono;
37
Philipp Schrader08537492021-01-23 16:17:55 -080038static const std::unordered_map<std::string, aos::starter::Command>
39 kCommandConversions{{"start", aos::starter::Command::START},
40 {"stop", aos::starter::Command::STOP},
41 {"restart", aos::starter::Command::RESTART}};
Tyler Chatowa79419d2020-08-12 20:12:11 -070042
James Kuszmaul293b2172021-11-10 16:20:48 -080043std::vector<const aos::Node *> InteractNodes(
44 const aos::Configuration *configuration) {
Austin Schuh43fceaf2021-10-16 14:20:22 -070045 if (!configuration->has_nodes()) {
James Kuszmaul293b2172021-11-10 16:20:48 -080046 return {nullptr};
Austin Schuh43fceaf2021-10-16 14:20:22 -070047 }
48
James Kuszmaul293b2172021-11-10 16:20:48 -080049 if (!FLAGS_node.empty()) {
50 CHECK(!FLAGS_all_nodes) << "Can't specify both --node and --all_nodes.";
51 return {aos::configuration::GetNode(configuration, FLAGS_node)};
52 }
53
54 if (FLAGS_all_nodes) {
55 return aos::configuration::GetNodes(configuration);
56 }
57
58 return {aos::configuration::GetMyNode(configuration)};
Austin Schuh43fceaf2021-10-16 14:20:22 -070059}
60
James Kuszmaul293b2172021-11-10 16:20:48 -080061std::vector<const aos::Node *> InteractNodesForApplication(
62 const aos::Configuration *config, std::string_view application_name) {
63 const std::vector<const aos::Node *> interact_nodes = InteractNodes(config);
64 std::vector<const aos::Node *> application_nodes;
65 std::vector<std::string> debug_node_names;
66 for (const aos::Node *node : interact_nodes) {
67 if (aos::configuration::GetApplication(config, node, application_name) !=
68 nullptr) {
69 application_nodes.push_back(node);
Austin Schuh43fceaf2021-10-16 14:20:22 -070070 }
James Kuszmaul293b2172021-11-10 16:20:48 -080071 if (node != nullptr) {
72 debug_node_names.push_back(node->name()->str());
73 }
Austin Schuh43fceaf2021-10-16 14:20:22 -070074 }
James Kuszmaul293b2172021-11-10 16:20:48 -080075
76 if (application_nodes.empty()) {
77 if (interact_nodes.size() == 1 && interact_nodes[0] == nullptr) {
78 std::cout << "Unknown application " << application_name << std::endl;
79 } else {
80 std::cout << "Unknown application " << application_name
81 << " on any of node(s) "
82 << absl::StrJoin(debug_node_names, ", ") << std::endl;
83 }
84 }
85 return application_nodes;
Austin Schuh43fceaf2021-10-16 14:20:22 -070086}
87
milind upadhyaya87957a2021-03-06 20:46:30 -080088void PrintKey() {
James Kuszmaulce3497e2023-02-26 12:40:26 -080089 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n", "Name", "Node", "State",
90 "PID", "Uptime", "Last Exit Code");
milind upadhyaya87957a2021-03-06 20:46:30 -080091}
92
Brian J Griglak6605beb2022-05-23 17:31:57 -060093std::vector<const aos::starter::ApplicationStatus *> SortApplications(
94 const aos::FlatbufferVector<aos::starter::Status> &status) {
95 std::vector<const aos::starter::ApplicationStatus *> sorted_statuses;
96 for (const aos::starter::ApplicationStatus *app_status :
97 *status.message().statuses()) {
98 sorted_statuses.push_back(app_status);
99 }
100 // If --sort flag not set, then return this unsorted vector as is.
101 if (FLAGS_sort.empty()) {
102 return sorted_statuses;
103 }
104
105 // Convert --sort flag to lowercase for testing below.
106 std::transform(FLAGS_sort.begin(), FLAGS_sort.end(), FLAGS_sort.begin(),
107 tolower);
108
109 // This function is called once for each node being reported upon, so there is
110 // no need to sort on node, it happens implicitly.
111
112 if (FLAGS_sort == "name") {
113 // Sort on name using std::string_view::operator< for lexicographic order.
114 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
115 [](const aos::starter::ApplicationStatus *lhs,
116 const aos::starter::ApplicationStatus *rhs) {
117 return lhs->name()->string_view() < rhs->name()->string_view();
118 });
119 } else if (FLAGS_sort == "state") {
120 // Sort on state first, and then name for apps in same state.
121 // ApplicationStatus::state is an enum, so need to call EnumNameState()
122 // convenience wrapper to convert enum to char*, and then wrap in
123 // std::string_view for lexicographic ordering.
124 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
125 [](const aos::starter::ApplicationStatus *lhs,
126 const aos::starter::ApplicationStatus *rhs) {
127 return (lhs->state() != rhs->state())
128 ? (std::string_view(
129 aos::starter::EnumNameState(lhs->state())) <
130 std::string_view(
131 aos::starter::EnumNameState(rhs->state())))
132 : (lhs->name()->string_view() <
133 rhs->name()->string_view());
134 });
135 } else if (FLAGS_sort == "pid") {
136 // Sort on pid first, and then name for when both apps are not running.
137 // If the app state is STOPPED, then it will not have a pid, so need to test
138 // that first. If only one app is STOPPED, then return Boolean state to put
139 // running apps before stopped.
140 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
141 [](const aos::starter::ApplicationStatus *lhs,
142 const aos::starter::ApplicationStatus *rhs) {
143 if (lhs->state() == aos::starter::State::STOPPED) {
144 if (rhs->state() == aos::starter::State::STOPPED) {
145 return lhs->name()->string_view() <
146 rhs->name()->string_view();
147 } else {
148 return false;
149 }
150 } else {
151 if (rhs->state() == aos::starter::State::STOPPED) {
152 return true;
153 } else {
154 return lhs->pid() < rhs->pid();
155 }
156 }
157 });
158 } else if (FLAGS_sort == "uptime") {
159 // Sort on last_start_time first, and then name for when both apps are not
160 // running, or have exact same start time. Only use last_start_time when app
161 // is not STOPPED. If only one app is STOPPED, then return Boolean state to
162 // put running apps before stopped.
163 std::sort(
164 sorted_statuses.begin(), sorted_statuses.end(),
165 [](const aos::starter::ApplicationStatus *lhs,
166 const aos::starter::ApplicationStatus *rhs) {
167 if (lhs->state() == aos::starter::State::STOPPED) {
168 if (rhs->state() == aos::starter::State::STOPPED) {
169 return lhs->name()->string_view() < rhs->name()->string_view();
170 } else {
171 return false;
172 }
173 } else {
174 if (rhs->state() == aos::starter::State::STOPPED) {
175 return true;
176 } else {
177 return (lhs->last_start_time() == rhs->last_start_time())
178 ? (lhs->name()->string_view() <
179 rhs->name()->string_view())
180 : (lhs->last_start_time() < rhs->last_start_time());
181 }
182 }
183 });
184 } else {
185 std::cerr << "Unknown sort criteria \"" << FLAGS_sort << "\"" << std::endl;
186 exit(1);
187 }
188
189 return sorted_statuses;
190}
191
milind upadhyaya87957a2021-03-06 20:46:30 -0800192void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
James Kuszmaul293b2172021-11-10 16:20:48 -0800193 const aos::monotonic_clock::time_point &time,
194 const aos::Node *node) {
milind upadhyay4272f382021-04-07 18:03:08 -0700195 const auto last_start_time = aos::monotonic_clock::time_point(
196 chrono::nanoseconds(app_status->last_start_time()));
milind upadhyaya87957a2021-03-06 20:46:30 -0800197 const auto time_running =
198 chrono::duration_cast<chrono::seconds>(time - last_start_time);
James Kuszmaulce3497e2023-02-26 12:40:26 -0800199 const std::string last_exit_code =
200 app_status->has_last_exit_code()
201 ? std::to_string(app_status->last_exit_code())
202 : "-";
Austin Schuhf4334002021-10-16 14:19:51 -0700203 if (app_status->state() == aos::starter::State::STOPPED) {
James Kuszmaulce3497e2023-02-26 12:40:26 -0800204 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n",
James Kuszmaul293b2172021-11-10 16:20:48 -0800205 app_status->name()->string_view(),
206 (node == nullptr) ? "none" : node->name()->string_view(),
James Kuszmaulce3497e2023-02-26 12:40:26 -0800207 aos::starter::EnumNameState(app_status->state()), "", "",
208 last_exit_code);
209 } else {
210 absl::PrintF(
211 "%-30s %-10s %-8s %-6d %-9s %-13s\n", app_status->name()->string_view(),
212 (node == nullptr) ? "none" : node->name()->string_view(),
213 aos::starter::EnumNameState(app_status->state()), app_status->pid(),
214 std::to_string(time_running.count()) + 's', last_exit_code);
Austin Schuhf4334002021-10-16 14:19:51 -0700215 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800216}
217
Austin Schuh43fceaf2021-10-16 14:20:22 -0700218// Prints the status for all applications.
219void GetAllStarterStatus(const aos::Configuration *config) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800220 PrintKey();
221 std::vector<const aos::Node *> missing_nodes;
222 for (const aos::Node *node : InteractNodes(config)) {
223 // Print status for all processes.
224 const auto optional_status = aos::starter::GetStarterStatus(config, node);
225 if (optional_status) {
226 const aos::FlatbufferVector<aos::starter::Status> &status =
227 optional_status->second;
228 const aos::monotonic_clock::time_point time = optional_status->first;
Brian J Griglak6605beb2022-05-23 17:31:57 -0600229 const auto &sorted_statuses = SortApplications(status);
James Kuszmaul293b2172021-11-10 16:20:48 -0800230 for (const aos::starter::ApplicationStatus *app_status :
Brian J Griglak6605beb2022-05-23 17:31:57 -0600231 sorted_statuses) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800232 PrintApplicationStatus(app_status, time, node);
233 }
234 } else {
235 missing_nodes.push_back(node);
Philipp Schrader08537492021-01-23 16:17:55 -0800236 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800237 }
238 for (const aos::Node *node : missing_nodes) {
239 if (node == nullptr) {
240 LOG(WARNING) << "No status found.";
241 } else {
242 LOG(WARNING) << "No status found for node "
243 << node->name()->string_view();
244 }
milind-u08dab882021-10-17 16:24:08 -0700245 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700246}
247
248// Handles the "status" command. Returns true if the help message should be
249// printed.
250bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
251 if (argc == 1) {
252 GetAllStarterStatus(config);
Philipp Schrader08537492021-01-23 16:17:55 -0800253 } else if (argc == 2) {
254 // Print status for the specified process.
milind upadhyay4272f382021-04-07 18:03:08 -0700255 const auto application_name =
256 aos::starter::FindApplication(argv[1], config);
Austin Schuh43fceaf2021-10-16 14:20:22 -0700257 if (application_name == "all") {
258 GetAllStarterStatus(config);
259 return false;
260 }
261
James Kuszmaul293b2172021-11-10 16:20:48 -0800262 const std::vector<const aos::Node *> application_nodes =
263 InteractNodesForApplication(config, application_name);
264 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700265 return false;
266 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800267 PrintKey();
James Kuszmaul293b2172021-11-10 16:20:48 -0800268 for (const aos::Node *node : application_nodes) {
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800269 auto optional_status =
270 aos::starter::GetStatus(application_name, config, node);
271 if (optional_status.has_value()) {
James Kuszmaul2ca441b2022-01-07 18:16:23 -0800272 PrintApplicationStatus(&optional_status.value().second.message(),
273 optional_status.value().first, node);
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800274 } else {
275 if (node != nullptr) {
276 LOG(ERROR) << "No status available yet for \"" << application_name
277 << "\" on node \"" << node->name()->string_view() << "\".";
278 } else {
279 LOG(ERROR) << "No status available yet for \"" << application_name
280 << "\".";
281 }
282 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800283 }
Philipp Schrader08537492021-01-23 16:17:55 -0800284 } else {
285 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
286 return true;
287 }
288 return false;
289}
Tyler Chatowa79419d2020-08-12 20:12:11 -0700290
Austin Schuh43fceaf2021-10-16 14:20:22 -0700291// Sends the provided command to all applications. Prints the success text on
292// success, and failure text on failure.
293void InteractWithAll(const aos::Configuration *config,
294 const aos::starter::Command command,
295 std::string_view success_text,
296 std::string_view failure_text) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800297 std::map<const aos::Node *,
298 std::unique_ptr<aos::FlatbufferVector<aos::starter::Status>>>
299 statuses;
300
301 for (const aos::Node *node : InteractNodes(config)) {
302 std::optional<std::pair<aos::monotonic_clock::time_point,
303 const aos::FlatbufferVector<aos::starter::Status>>>
304 optional_status = aos::starter::GetStarterStatus(config, node);
305 if (optional_status.has_value()) {
306 statuses[node] =
307 std::make_unique<aos::FlatbufferVector<aos::starter::Status>>(
308 optional_status.value().second);
309 } else {
310 if (node == nullptr) {
311 LOG(WARNING) << "Starter not running";
312 } else {
313 LOG(WARNING) << "Starter not running on node "
314 << node->name()->string_view();
315 }
316 }
317 }
318
319 if (!statuses.empty()) {
320 std::vector<aos::starter::ApplicationCommand> commands;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700321
322 for (const aos::Application *application : *config->applications()) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800323 const std::string_view application_name =
324 application->name()->string_view();
325 const std::vector<const aos::Node *> application_nodes =
326 InteractNodesForApplication(config, application_name);
327 // Ignore any applications which aren't supposed to be started.
328 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700329 continue;
330 }
331
James Kuszmaul293b2172021-11-10 16:20:48 -0800332 std::vector<const aos::Node *> running_nodes;
333 if (application->autostart()) {
334 running_nodes = application_nodes;
335 } else {
336 for (const aos::Node *node : application_nodes) {
337 const aos::starter::ApplicationStatus *application_status =
338 aos::starter::FindApplicationStatus(statuses[node]->message(),
339 application_name);
340 if (application_status->state() == aos::starter::State::STOPPED) {
341 if (node == nullptr) {
342 std::cout << "Skipping " << application_name
343 << " because it is STOPPED\n";
344 } else {
345 std::cout << "Skipping " << application_name << " on "
346 << node->name()->string_view()
347 << " because it is STOPPED\n";
348 }
349 continue;
350 } else {
351 running_nodes.push_back(node);
352 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700353 }
354 }
355
James Kuszmaul293b2172021-11-10 16:20:48 -0800356 if (!running_nodes.empty()) {
357 commands.emplace_back(aos::starter::ApplicationCommand{
358 command, application_name, running_nodes});
359 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700360 }
361
362 // Restart each running process
363 if (aos::starter::SendCommandBlocking(commands, config,
364 chrono::seconds(5))) {
365 std::cout << success_text << "all \n";
366 } else {
367 std::cout << failure_text << "all \n";
368 }
369 } else {
James Kuszmaul293b2172021-11-10 16:20:48 -0800370 LOG(WARNING) << "None of the starters we care about are running.";
Austin Schuh43fceaf2021-10-16 14:20:22 -0700371 }
372}
373
374// Handles the "start", "stop", and "restart" commands. Returns true if the
375// help message should be printed.
Philipp Schrader08537492021-01-23 16:17:55 -0800376bool InteractWithProgram(int argc, char **argv,
377 const aos::Configuration *config) {
378 const char *command_string = argv[0];
Philipp Schrader08537492021-01-23 16:17:55 -0800379 if (argc != 2) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700380 LOG(ERROR)
381 << "The \"" << command_string
382 << "\" command requires an application name or 'all' as an argument.";
Philipp Schrader08537492021-01-23 16:17:55 -0800383 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700384 }
385
Philipp Schrader08537492021-01-23 16:17:55 -0800386 const auto command_search = kCommandConversions.find(command_string);
387 CHECK(command_search != kCommandConversions.end())
388 << "Internal error: \"" << command_string
389 << "\" is not in kCommandConversions.";
Philipp Schrader08537492021-01-23 16:17:55 -0800390 const aos::starter::Command command = command_search->second;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700391
392 std::string_view success_text;
393 const std::string failure_text =
394 std::string("Failed to ") + std::string(command_string) + " ";
395 switch (command) {
396 case aos::starter::Command::START:
397 success_text = "Successfully started ";
398 break;
399 case aos::starter::Command::STOP:
400 success_text = "Successfully stopped ";
401 break;
402 case aos::starter::Command::RESTART:
403 success_text = "Successfully restarted ";
404 break;
405 }
406
407 const std::string_view application_name =
408 aos::starter::FindApplication(argv[1], config);
409 if (application_name == "all") {
410 InteractWithAll(config, command, success_text, failure_text);
411 return false;
412 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800413
414 const std::vector<const aos::Node *> application_nodes =
415 InteractNodesForApplication(config, application_name);
416 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700417 return false;
418 }
419
Philipp Schrader08537492021-01-23 16:17:55 -0800420 if (aos::starter::SendCommandBlocking(command, application_name, config,
James Kuszmaul293b2172021-11-10 16:20:48 -0800421 chrono::seconds(5),
422 application_nodes)) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700423 std::cout << success_text << application_name << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700424 } else {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700425 std::cout << failure_text << application_name << '\n';
Jacob Ismael6388db92021-06-28 22:51:24 -0700426 }
427 return false;
428}
429
Austin Schuh33cc4162021-10-16 14:20:28 -0700430bool Help(int /*argc*/, char ** /*argv*/,
431 const aos::Configuration * /*config*/);
432
Philipp Schrader08537492021-01-23 16:17:55 -0800433// This is the set of subcommands we support. Each subcommand accepts argc and
434// argv from its own point of view. So argv[0] is always the name of the
435// subcommand. argv[1] and up are the arguments to the subcommand.
436// The subcommand returns true if there was an error parsing the command line
437// arguments. It returns false when the command line arguments are parsed
438// successfully.
Austin Schuh33cc4162021-10-16 14:20:28 -0700439static const std::vector<
440 std::tuple<std::string,
441 std::function<bool(int argc, char **argv,
442 const aos::Configuration *config)>,
443 std::string_view>>
444 kCommands{
445 {"help", Help, ""},
446 {"status", GetStarterStatus,
447 " [application], Returns the status of the provided application, "
448 "or all applications by default"},
449 {"start", InteractWithProgram,
450 " application, Starts the provided application, "
451 "or all applications if all is provided"},
452 {"stop", InteractWithProgram,
453 " application, Stops the provided application, "
454 "or all applications if all is provided"},
455 {"restart", InteractWithProgram,
456 " application, Restarts the provided application, "
457 "or all applications if all is provided"}};
458
459bool Help(int /*argc*/, char ** /*argv*/,
460 const aos::Configuration * /*config*/) {
461 std::cout << "Valid commands are:" << std::endl;
462 for (auto entry : kCommands) {
463 std::cout << " - " << std::get<0>(entry) << std::get<2>(entry) << std::endl;
464 }
465 return false;
466}
Philipp Schrader08537492021-01-23 16:17:55 -0800467
milind-u08dab882021-10-17 16:24:08 -0700468void Autocomplete(int argc, char **argv, const aos::Configuration *config) {
469 const std::string_view command = (argc >= 2 ? argv[1] : "");
470 const std::string_view app_name = (argc >= 3 ? argv[2] : "");
471
472 std::cout << "COMPREPLY=(";
473 if (FLAGS__bash_autocomplete_word == command) {
474 // Autocomplete the starter command
475 for (const auto &entry : kCommands) {
476 if (std::get<0>(entry).find(command) == 0) {
477 std::cout << '\'' << std::get<0>(entry) << "' ";
478 }
479 }
480 } else {
481 // Autocomplete the app name
482 for (const auto *app : *config->applications()) {
483 if (app->has_name() && app->name()->string_view().find(app_name) == 0) {
484 std::cout << '\'' << app->name()->string_view() << "' ";
485 }
486 }
487
488 // Autocomplete with "all"
milind-u95296dd2021-10-19 07:42:17 -0700489 if (std::string_view("all").find(app_name) == 0) {
milind-u08dab882021-10-17 16:24:08 -0700490 std::cout << "'all'";
491 }
492 }
493 std::cout << ')';
494}
495
Philipp Schrader08537492021-01-23 16:17:55 -0800496} // namespace
497
498int main(int argc, char **argv) {
499 aos::InitGoogle(&argc, &argv);
500
501 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
502 aos::configuration::ReadConfig(FLAGS_config);
503
milind-u08dab882021-10-17 16:24:08 -0700504 if (FLAGS__bash_autocomplete) {
505 Autocomplete(argc, argv, &config.message());
506 return 0;
507 }
508
Philipp Schrader08537492021-01-23 16:17:55 -0800509 bool parsing_failed = false;
510
511 if (argc < 2) {
512 parsing_failed = true;
513 } else {
514 const char *command = argv[1];
Austin Schuh33cc4162021-10-16 14:20:28 -0700515 auto it = std::find_if(
516 kCommands.begin(), kCommands.end(),
517 [command](const std::tuple<
518 std::string,
519 std::function<bool(int argc, char **argv,
520 const aos::Configuration *config)>,
521 std::string_view> &t) { return std::get<0>(t) == command; });
522
Philipp Schrader08537492021-01-23 16:17:55 -0800523 if (it == kCommands.end()) {
524 parsing_failed = true;
525 } else {
Austin Schuh33cc4162021-10-16 14:20:28 -0700526 parsing_failed = std::get<1>(*it)(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800527 }
528 }
529
530 if (parsing_failed) {
Austin Schuh33cc4162021-10-16 14:20:28 -0700531 Help(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800532 return 1;
533 }
534
535 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700536}