blob: b79485a8e9e5c4977b606a3d296d4e9f1b87a819 [file] [log] [blame]
Stephan Pleinesf581a072024-05-23 20:59:27 -07001#include <ctype.h>
2#include <stdlib.h>
3
Brian J Griglak6605beb2022-05-23 17:31:57 -06004#include <algorithm>
Tyler Chatowa79419d2020-08-12 20:12:11 -07005#include <chrono>
Stephan Pleinesf581a072024-05-23 20:59:27 -07006#include <compare>
Philipp Schrader08537492021-01-23 16:17:55 -08007#include <functional>
Tyler Chatowa79419d2020-08-12 20:12:11 -07008#include <iostream>
Stephan Pleinesf581a072024-05-23 20:59:27 -07009#include <map>
10#include <memory>
milind upadhyaya87957a2021-03-06 20:46:30 -080011#include <optional>
Stephan Pleinesf581a072024-05-23 20:59:27 -070012#include <string>
milind upadhyay4272f382021-04-07 18:03:08 -070013#include <string_view>
Stephan Pleinesf581a072024-05-23 20:59:27 -070014#include <tuple>
Tyler Chatowa79419d2020-08-12 20:12:11 -070015#include <unordered_map>
Stephan Pleinesf581a072024-05-23 20:59:27 -070016#include <utility>
17#include <vector>
Tyler Chatowa79419d2020-08-12 20:12:11 -070018
Philipp Schrader08537492021-01-23 16:17:55 -080019#include "absl/strings/str_format.h"
James Kuszmaul293b2172021-11-10 16:20:48 -080020#include "absl/strings/str_join.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070021#include "flatbuffers/string.h"
22#include "flatbuffers/vector.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070023#include "gflags/gflags.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070024#include "glog/logging.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070025
Stephan Pleinesf581a072024-05-23 20:59:27 -070026#include "aos/configuration.h"
27#include "aos/flatbuffers.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070028#include "aos/init.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070029#include "aos/starter/starter_generated.h"
30#include "aos/starter/starter_rpc_generated.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070031#include "aos/starter/starter_rpc_lib.h"
milind upadhyaya87957a2021-03-06 20:46:30 -080032#include "aos/time/time.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070033
Austin Schuh8e2dfc62022-08-17 16:36:00 -070034DEFINE_string(config, "aos_config.json", "File path of aos configuration");
James Kuszmaul293b2172021-11-10 16:20:48 -080035// TODO(james): Bash autocompletion for node names.
36DEFINE_string(
37 node, "",
38 "Node to interact with. If empty, just interact with local node.");
39DEFINE_bool(all_nodes, false, "Interact with all nodes.");
Tyler Chatowa79419d2020-08-12 20:12:11 -070040
milind-u08dab882021-10-17 16:24:08 -070041DEFINE_bool(_bash_autocomplete, false,
42 "Internal use: Outputs commands or applications for use with "
43 "autocomplete script.");
44DEFINE_string(_bash_autocomplete_word, "",
45 "Internal use: Current word being autocompleted");
Brian J Griglak6605beb2022-05-23 17:31:57 -060046DEFINE_string(sort, "name",
47 "The name of the column to sort processes by. "
48 "Can be \"name\", \"state\", \"pid\", or \"uptime\".");
milind-u08dab882021-10-17 16:24:08 -070049
Philipp Schrader08537492021-01-23 16:17:55 -080050namespace {
Tyler Chatowa79419d2020-08-12 20:12:11 -070051
milind upadhyaya87957a2021-03-06 20:46:30 -080052namespace chrono = std::chrono;
53
Philipp Schrader08537492021-01-23 16:17:55 -080054static const std::unordered_map<std::string, aos::starter::Command>
55 kCommandConversions{{"start", aos::starter::Command::START},
56 {"stop", aos::starter::Command::STOP},
57 {"restart", aos::starter::Command::RESTART}};
Tyler Chatowa79419d2020-08-12 20:12:11 -070058
James Kuszmaul293b2172021-11-10 16:20:48 -080059std::vector<const aos::Node *> InteractNodes(
60 const aos::Configuration *configuration) {
Austin Schuh43fceaf2021-10-16 14:20:22 -070061 if (!configuration->has_nodes()) {
James Kuszmaul293b2172021-11-10 16:20:48 -080062 return {nullptr};
Austin Schuh43fceaf2021-10-16 14:20:22 -070063 }
64
James Kuszmaul293b2172021-11-10 16:20:48 -080065 if (!FLAGS_node.empty()) {
66 CHECK(!FLAGS_all_nodes) << "Can't specify both --node and --all_nodes.";
67 return {aos::configuration::GetNode(configuration, FLAGS_node)};
68 }
69
70 if (FLAGS_all_nodes) {
71 return aos::configuration::GetNodes(configuration);
72 }
73
74 return {aos::configuration::GetMyNode(configuration)};
Austin Schuh43fceaf2021-10-16 14:20:22 -070075}
76
James Kuszmaul293b2172021-11-10 16:20:48 -080077std::vector<const aos::Node *> InteractNodesForApplication(
78 const aos::Configuration *config, std::string_view application_name) {
79 const std::vector<const aos::Node *> interact_nodes = InteractNodes(config);
80 std::vector<const aos::Node *> application_nodes;
81 std::vector<std::string> debug_node_names;
82 for (const aos::Node *node : interact_nodes) {
83 if (aos::configuration::GetApplication(config, node, application_name) !=
84 nullptr) {
85 application_nodes.push_back(node);
Austin Schuh43fceaf2021-10-16 14:20:22 -070086 }
James Kuszmaul293b2172021-11-10 16:20:48 -080087 if (node != nullptr) {
88 debug_node_names.push_back(node->name()->str());
89 }
Austin Schuh43fceaf2021-10-16 14:20:22 -070090 }
James Kuszmaul293b2172021-11-10 16:20:48 -080091
92 if (application_nodes.empty()) {
93 if (interact_nodes.size() == 1 && interact_nodes[0] == nullptr) {
94 std::cout << "Unknown application " << application_name << std::endl;
95 } else {
96 std::cout << "Unknown application " << application_name
97 << " on any of node(s) "
98 << absl::StrJoin(debug_node_names, ", ") << std::endl;
99 }
100 }
101 return application_nodes;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700102}
103
milind upadhyaya87957a2021-03-06 20:46:30 -0800104void PrintKey() {
James Kuszmaulce3497e2023-02-26 12:40:26 -0800105 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n", "Name", "Node", "State",
106 "PID", "Uptime", "Last Exit Code");
milind upadhyaya87957a2021-03-06 20:46:30 -0800107}
108
Brian J Griglak6605beb2022-05-23 17:31:57 -0600109std::vector<const aos::starter::ApplicationStatus *> SortApplications(
110 const aos::FlatbufferVector<aos::starter::Status> &status) {
111 std::vector<const aos::starter::ApplicationStatus *> sorted_statuses;
112 for (const aos::starter::ApplicationStatus *app_status :
113 *status.message().statuses()) {
114 sorted_statuses.push_back(app_status);
115 }
116 // If --sort flag not set, then return this unsorted vector as is.
117 if (FLAGS_sort.empty()) {
118 return sorted_statuses;
119 }
120
121 // Convert --sort flag to lowercase for testing below.
122 std::transform(FLAGS_sort.begin(), FLAGS_sort.end(), FLAGS_sort.begin(),
123 tolower);
124
125 // This function is called once for each node being reported upon, so there is
126 // no need to sort on node, it happens implicitly.
127
128 if (FLAGS_sort == "name") {
129 // Sort on name using std::string_view::operator< for lexicographic order.
130 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
131 [](const aos::starter::ApplicationStatus *lhs,
132 const aos::starter::ApplicationStatus *rhs) {
133 return lhs->name()->string_view() < rhs->name()->string_view();
134 });
135 } else if (FLAGS_sort == "state") {
136 // Sort on state first, and then name for apps in same state.
137 // ApplicationStatus::state is an enum, so need to call EnumNameState()
138 // convenience wrapper to convert enum to char*, and then wrap in
139 // std::string_view for lexicographic ordering.
140 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
141 [](const aos::starter::ApplicationStatus *lhs,
142 const aos::starter::ApplicationStatus *rhs) {
143 return (lhs->state() != rhs->state())
144 ? (std::string_view(
145 aos::starter::EnumNameState(lhs->state())) <
146 std::string_view(
147 aos::starter::EnumNameState(rhs->state())))
148 : (lhs->name()->string_view() <
149 rhs->name()->string_view());
150 });
151 } else if (FLAGS_sort == "pid") {
152 // Sort on pid first, and then name for when both apps are not running.
153 // If the app state is STOPPED, then it will not have a pid, so need to test
154 // that first. If only one app is STOPPED, then return Boolean state to put
155 // running apps before stopped.
156 std::sort(sorted_statuses.begin(), sorted_statuses.end(),
157 [](const aos::starter::ApplicationStatus *lhs,
158 const aos::starter::ApplicationStatus *rhs) {
159 if (lhs->state() == aos::starter::State::STOPPED) {
160 if (rhs->state() == aos::starter::State::STOPPED) {
161 return lhs->name()->string_view() <
162 rhs->name()->string_view();
163 } else {
164 return false;
165 }
166 } else {
167 if (rhs->state() == aos::starter::State::STOPPED) {
168 return true;
169 } else {
170 return lhs->pid() < rhs->pid();
171 }
172 }
173 });
174 } else if (FLAGS_sort == "uptime") {
175 // Sort on last_start_time first, and then name for when both apps are not
176 // running, or have exact same start time. Only use last_start_time when app
177 // is not STOPPED. If only one app is STOPPED, then return Boolean state to
178 // put running apps before stopped.
179 std::sort(
180 sorted_statuses.begin(), sorted_statuses.end(),
181 [](const aos::starter::ApplicationStatus *lhs,
182 const aos::starter::ApplicationStatus *rhs) {
183 if (lhs->state() == aos::starter::State::STOPPED) {
184 if (rhs->state() == aos::starter::State::STOPPED) {
185 return lhs->name()->string_view() < rhs->name()->string_view();
186 } else {
187 return false;
188 }
189 } else {
190 if (rhs->state() == aos::starter::State::STOPPED) {
191 return true;
192 } else {
193 return (lhs->last_start_time() == rhs->last_start_time())
194 ? (lhs->name()->string_view() <
195 rhs->name()->string_view())
196 : (lhs->last_start_time() < rhs->last_start_time());
197 }
198 }
199 });
200 } else {
201 std::cerr << "Unknown sort criteria \"" << FLAGS_sort << "\"" << std::endl;
202 exit(1);
203 }
204
205 return sorted_statuses;
206}
207
milind upadhyaya87957a2021-03-06 20:46:30 -0800208void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
James Kuszmaul293b2172021-11-10 16:20:48 -0800209 const aos::monotonic_clock::time_point &time,
210 const aos::Node *node) {
milind upadhyay4272f382021-04-07 18:03:08 -0700211 const auto last_start_time = aos::monotonic_clock::time_point(
212 chrono::nanoseconds(app_status->last_start_time()));
milind upadhyaya87957a2021-03-06 20:46:30 -0800213 const auto time_running =
214 chrono::duration_cast<chrono::seconds>(time - last_start_time);
James Kuszmaulce3497e2023-02-26 12:40:26 -0800215 const std::string last_exit_code =
216 app_status->has_last_exit_code()
217 ? std::to_string(app_status->last_exit_code())
218 : "-";
Austin Schuhf4334002021-10-16 14:19:51 -0700219 if (app_status->state() == aos::starter::State::STOPPED) {
James Kuszmaulce3497e2023-02-26 12:40:26 -0800220 absl::PrintF("%-30s %-10s %-8s %-6s %-9s %-13s\n",
James Kuszmaul293b2172021-11-10 16:20:48 -0800221 app_status->name()->string_view(),
222 (node == nullptr) ? "none" : node->name()->string_view(),
James Kuszmaulce3497e2023-02-26 12:40:26 -0800223 aos::starter::EnumNameState(app_status->state()), "", "",
224 last_exit_code);
225 } else {
226 absl::PrintF(
227 "%-30s %-10s %-8s %-6d %-9s %-13s\n", app_status->name()->string_view(),
228 (node == nullptr) ? "none" : node->name()->string_view(),
229 aos::starter::EnumNameState(app_status->state()), app_status->pid(),
230 std::to_string(time_running.count()) + 's', last_exit_code);
Austin Schuhf4334002021-10-16 14:19:51 -0700231 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800232}
233
Austin Schuh43fceaf2021-10-16 14:20:22 -0700234// Prints the status for all applications.
235void GetAllStarterStatus(const aos::Configuration *config) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800236 PrintKey();
237 std::vector<const aos::Node *> missing_nodes;
238 for (const aos::Node *node : InteractNodes(config)) {
239 // Print status for all processes.
240 const auto optional_status = aos::starter::GetStarterStatus(config, node);
241 if (optional_status) {
242 const aos::FlatbufferVector<aos::starter::Status> &status =
243 optional_status->second;
244 const aos::monotonic_clock::time_point time = optional_status->first;
Brian J Griglak6605beb2022-05-23 17:31:57 -0600245 const auto &sorted_statuses = SortApplications(status);
James Kuszmaul293b2172021-11-10 16:20:48 -0800246 for (const aos::starter::ApplicationStatus *app_status :
Brian J Griglak6605beb2022-05-23 17:31:57 -0600247 sorted_statuses) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800248 PrintApplicationStatus(app_status, time, node);
249 }
250 } else {
251 missing_nodes.push_back(node);
Philipp Schrader08537492021-01-23 16:17:55 -0800252 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800253 }
254 for (const aos::Node *node : missing_nodes) {
255 if (node == nullptr) {
256 LOG(WARNING) << "No status found.";
257 } else {
258 LOG(WARNING) << "No status found for node "
259 << node->name()->string_view();
260 }
milind-u08dab882021-10-17 16:24:08 -0700261 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700262}
263
264// Handles the "status" command. Returns true if the help message should be
265// printed.
266bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
267 if (argc == 1) {
268 GetAllStarterStatus(config);
Philipp Schrader08537492021-01-23 16:17:55 -0800269 } else if (argc == 2) {
270 // Print status for the specified process.
milind upadhyay4272f382021-04-07 18:03:08 -0700271 const auto application_name =
272 aos::starter::FindApplication(argv[1], config);
Austin Schuh43fceaf2021-10-16 14:20:22 -0700273 if (application_name == "all") {
274 GetAllStarterStatus(config);
275 return false;
276 }
277
James Kuszmaul293b2172021-11-10 16:20:48 -0800278 const std::vector<const aos::Node *> application_nodes =
279 InteractNodesForApplication(config, application_name);
280 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700281 return false;
282 }
milind upadhyaya87957a2021-03-06 20:46:30 -0800283 PrintKey();
James Kuszmaul293b2172021-11-10 16:20:48 -0800284 for (const aos::Node *node : application_nodes) {
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800285 auto optional_status =
286 aos::starter::GetStatus(application_name, config, node);
287 if (optional_status.has_value()) {
James Kuszmaul2ca441b2022-01-07 18:16:23 -0800288 PrintApplicationStatus(&optional_status.value().second.message(),
289 optional_status.value().first, node);
James Kuszmaule4bb0a22022-01-07 18:14:43 -0800290 } else {
291 if (node != nullptr) {
292 LOG(ERROR) << "No status available yet for \"" << application_name
293 << "\" on node \"" << node->name()->string_view() << "\".";
294 } else {
295 LOG(ERROR) << "No status available yet for \"" << application_name
296 << "\".";
297 }
298 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800299 }
Philipp Schrader08537492021-01-23 16:17:55 -0800300 } else {
301 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
302 return true;
303 }
304 return false;
305}
Tyler Chatowa79419d2020-08-12 20:12:11 -0700306
Austin Schuh43fceaf2021-10-16 14:20:22 -0700307// Sends the provided command to all applications. Prints the success text on
308// success, and failure text on failure.
309void InteractWithAll(const aos::Configuration *config,
310 const aos::starter::Command command,
311 std::string_view success_text,
312 std::string_view failure_text) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800313 std::map<const aos::Node *,
314 std::unique_ptr<aos::FlatbufferVector<aos::starter::Status>>>
315 statuses;
316
317 for (const aos::Node *node : InteractNodes(config)) {
318 std::optional<std::pair<aos::monotonic_clock::time_point,
319 const aos::FlatbufferVector<aos::starter::Status>>>
320 optional_status = aos::starter::GetStarterStatus(config, node);
321 if (optional_status.has_value()) {
322 statuses[node] =
323 std::make_unique<aos::FlatbufferVector<aos::starter::Status>>(
324 optional_status.value().second);
325 } else {
326 if (node == nullptr) {
327 LOG(WARNING) << "Starter not running";
328 } else {
329 LOG(WARNING) << "Starter not running on node "
330 << node->name()->string_view();
331 }
332 }
333 }
334
335 if (!statuses.empty()) {
336 std::vector<aos::starter::ApplicationCommand> commands;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700337
338 for (const aos::Application *application : *config->applications()) {
James Kuszmaul293b2172021-11-10 16:20:48 -0800339 const std::string_view application_name =
340 application->name()->string_view();
341 const std::vector<const aos::Node *> application_nodes =
342 InteractNodesForApplication(config, application_name);
343 // Ignore any applications which aren't supposed to be started.
344 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700345 continue;
346 }
347
James Kuszmaul293b2172021-11-10 16:20:48 -0800348 std::vector<const aos::Node *> running_nodes;
349 if (application->autostart()) {
350 running_nodes = application_nodes;
351 } else {
352 for (const aos::Node *node : application_nodes) {
353 const aos::starter::ApplicationStatus *application_status =
354 aos::starter::FindApplicationStatus(statuses[node]->message(),
355 application_name);
356 if (application_status->state() == aos::starter::State::STOPPED) {
357 if (node == nullptr) {
358 std::cout << "Skipping " << application_name
359 << " because it is STOPPED\n";
360 } else {
361 std::cout << "Skipping " << application_name << " on "
362 << node->name()->string_view()
363 << " because it is STOPPED\n";
364 }
365 continue;
366 } else {
367 running_nodes.push_back(node);
368 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700369 }
370 }
371
James Kuszmaul293b2172021-11-10 16:20:48 -0800372 if (!running_nodes.empty()) {
373 commands.emplace_back(aos::starter::ApplicationCommand{
374 command, application_name, running_nodes});
375 }
Austin Schuh43fceaf2021-10-16 14:20:22 -0700376 }
377
378 // Restart each running process
379 if (aos::starter::SendCommandBlocking(commands, config,
380 chrono::seconds(5))) {
381 std::cout << success_text << "all \n";
382 } else {
383 std::cout << failure_text << "all \n";
384 }
385 } else {
James Kuszmaul293b2172021-11-10 16:20:48 -0800386 LOG(WARNING) << "None of the starters we care about are running.";
Austin Schuh43fceaf2021-10-16 14:20:22 -0700387 }
388}
389
390// Handles the "start", "stop", and "restart" commands. Returns true if the
391// help message should be printed.
Philipp Schrader08537492021-01-23 16:17:55 -0800392bool InteractWithProgram(int argc, char **argv,
393 const aos::Configuration *config) {
394 const char *command_string = argv[0];
Philipp Schrader08537492021-01-23 16:17:55 -0800395 if (argc != 2) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700396 LOG(ERROR)
397 << "The \"" << command_string
398 << "\" command requires an application name or 'all' as an argument.";
Philipp Schrader08537492021-01-23 16:17:55 -0800399 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700400 }
401
Philipp Schrader08537492021-01-23 16:17:55 -0800402 const auto command_search = kCommandConversions.find(command_string);
403 CHECK(command_search != kCommandConversions.end())
404 << "Internal error: \"" << command_string
405 << "\" is not in kCommandConversions.";
Philipp Schrader08537492021-01-23 16:17:55 -0800406 const aos::starter::Command command = command_search->second;
Austin Schuh43fceaf2021-10-16 14:20:22 -0700407
408 std::string_view success_text;
409 const std::string failure_text =
410 std::string("Failed to ") + std::string(command_string) + " ";
411 switch (command) {
412 case aos::starter::Command::START:
413 success_text = "Successfully started ";
414 break;
415 case aos::starter::Command::STOP:
416 success_text = "Successfully stopped ";
417 break;
418 case aos::starter::Command::RESTART:
419 success_text = "Successfully restarted ";
420 break;
421 }
422
423 const std::string_view application_name =
424 aos::starter::FindApplication(argv[1], config);
425 if (application_name == "all") {
426 InteractWithAll(config, command, success_text, failure_text);
427 return false;
428 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800429
430 const std::vector<const aos::Node *> application_nodes =
431 InteractNodesForApplication(config, application_name);
432 if (application_nodes.empty()) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700433 return false;
434 }
435
Philipp Schrader08537492021-01-23 16:17:55 -0800436 if (aos::starter::SendCommandBlocking(command, application_name, config,
James Kuszmaul293b2172021-11-10 16:20:48 -0800437 chrono::seconds(5),
438 application_nodes)) {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700439 std::cout << success_text << application_name << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700440 } else {
Austin Schuh43fceaf2021-10-16 14:20:22 -0700441 std::cout << failure_text << application_name << '\n';
Jacob Ismael6388db92021-06-28 22:51:24 -0700442 }
443 return false;
444}
445
Austin Schuh33cc4162021-10-16 14:20:28 -0700446bool Help(int /*argc*/, char ** /*argv*/,
447 const aos::Configuration * /*config*/);
448
Philipp Schrader08537492021-01-23 16:17:55 -0800449// This is the set of subcommands we support. Each subcommand accepts argc and
450// argv from its own point of view. So argv[0] is always the name of the
451// subcommand. argv[1] and up are the arguments to the subcommand.
452// The subcommand returns true if there was an error parsing the command line
453// arguments. It returns false when the command line arguments are parsed
454// successfully.
Austin Schuh33cc4162021-10-16 14:20:28 -0700455static const std::vector<
456 std::tuple<std::string,
457 std::function<bool(int argc, char **argv,
458 const aos::Configuration *config)>,
459 std::string_view>>
460 kCommands{
461 {"help", Help, ""},
462 {"status", GetStarterStatus,
463 " [application], Returns the status of the provided application, "
464 "or all applications by default"},
465 {"start", InteractWithProgram,
466 " application, Starts the provided application, "
467 "or all applications if all is provided"},
468 {"stop", InteractWithProgram,
469 " application, Stops the provided application, "
470 "or all applications if all is provided"},
471 {"restart", InteractWithProgram,
472 " application, Restarts the provided application, "
473 "or all applications if all is provided"}};
474
475bool Help(int /*argc*/, char ** /*argv*/,
476 const aos::Configuration * /*config*/) {
477 std::cout << "Valid commands are:" << std::endl;
478 for (auto entry : kCommands) {
479 std::cout << " - " << std::get<0>(entry) << std::get<2>(entry) << std::endl;
480 }
481 return false;
482}
Philipp Schrader08537492021-01-23 16:17:55 -0800483
milind-u08dab882021-10-17 16:24:08 -0700484void Autocomplete(int argc, char **argv, const aos::Configuration *config) {
485 const std::string_view command = (argc >= 2 ? argv[1] : "");
486 const std::string_view app_name = (argc >= 3 ? argv[2] : "");
487
488 std::cout << "COMPREPLY=(";
489 if (FLAGS__bash_autocomplete_word == command) {
490 // Autocomplete the starter command
491 for (const auto &entry : kCommands) {
492 if (std::get<0>(entry).find(command) == 0) {
493 std::cout << '\'' << std::get<0>(entry) << "' ";
494 }
495 }
496 } else {
497 // Autocomplete the app name
498 for (const auto *app : *config->applications()) {
499 if (app->has_name() && app->name()->string_view().find(app_name) == 0) {
500 std::cout << '\'' << app->name()->string_view() << "' ";
501 }
502 }
503
504 // Autocomplete with "all"
milind-u95296dd2021-10-19 07:42:17 -0700505 if (std::string_view("all").find(app_name) == 0) {
milind-u08dab882021-10-17 16:24:08 -0700506 std::cout << "'all'";
507 }
508 }
509 std::cout << ')';
510}
511
Philipp Schrader08537492021-01-23 16:17:55 -0800512} // namespace
513
514int main(int argc, char **argv) {
515 aos::InitGoogle(&argc, &argv);
516
517 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
518 aos::configuration::ReadConfig(FLAGS_config);
519
milind-u08dab882021-10-17 16:24:08 -0700520 if (FLAGS__bash_autocomplete) {
521 Autocomplete(argc, argv, &config.message());
522 return 0;
523 }
524
Philipp Schrader08537492021-01-23 16:17:55 -0800525 bool parsing_failed = false;
526
527 if (argc < 2) {
528 parsing_failed = true;
529 } else {
530 const char *command = argv[1];
Austin Schuh33cc4162021-10-16 14:20:28 -0700531 auto it = std::find_if(
532 kCommands.begin(), kCommands.end(),
533 [command](const std::tuple<
534 std::string,
535 std::function<bool(int argc, char **argv,
536 const aos::Configuration *config)>,
537 std::string_view> &t) { return std::get<0>(t) == command; });
538
Philipp Schrader08537492021-01-23 16:17:55 -0800539 if (it == kCommands.end()) {
540 parsing_failed = true;
541 } else {
Austin Schuh33cc4162021-10-16 14:20:28 -0700542 parsing_failed = std::get<1>(*it)(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800543 }
544 }
545
546 if (parsing_failed) {
Austin Schuh33cc4162021-10-16 14:20:28 -0700547 Help(argc - 1, argv + 1, &config.message());
Philipp Schrader08537492021-01-23 16:17:55 -0800548 return 1;
549 }
550
551 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700552}