blob: e9b6ed530fa6a7977d3cf46218ee3907c9647d1c [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
milind upadhyaya87957a2021-03-06 20:46:30 -080026void PrintKey() {
milind upadhyay4272f382021-04-07 18:03:08 -070027 absl::PrintF("%-30s %-30s %s\n\n", "Name", "Time since last started",
28 "State");
milind upadhyaya87957a2021-03-06 20:46:30 -080029}
30
31void PrintApplicationStatus(const aos::starter::ApplicationStatus *app_status,
milind upadhyay4272f382021-04-07 18:03:08 -070032 const aos::monotonic_clock::time_point &time) {
33 const auto last_start_time = aos::monotonic_clock::time_point(
34 chrono::nanoseconds(app_status->last_start_time()));
milind upadhyaya87957a2021-03-06 20:46:30 -080035 const auto time_running =
36 chrono::duration_cast<chrono::seconds>(time - last_start_time);
37 absl::PrintF("%-30s %-30s %s\n", app_status->name()->string_view(),
38 std::to_string(time_running.count()) + 's',
39 aos::starter::EnumNameState(app_status->state()));
40}
41
Philipp Schrader08537492021-01-23 16:17:55 -080042bool GetStarterStatus(int argc, char **argv, const aos::Configuration *config) {
43 if (argc == 1) {
44 // Print status for all processes.
milind upadhyaya87957a2021-03-06 20:46:30 -080045 const auto optional_status = aos::starter::GetStarterStatus(config);
46 if (optional_status) {
47 auto status = *optional_status;
48 const auto time = aos::monotonic_clock::now();
49 PrintKey();
50 for (const aos::starter::ApplicationStatus *app_status :
51 *status.message().statuses()) {
52 PrintApplicationStatus(app_status, time);
53 }
54 } else {
55 LOG(WARNING) << "No status found";
Philipp Schrader08537492021-01-23 16:17:55 -080056 }
57 } else if (argc == 2) {
58 // Print status for the specified process.
milind upadhyay4272f382021-04-07 18:03:08 -070059 const auto application_name =
60 aos::starter::FindApplication(argv[1], config);
Philipp Schrader08537492021-01-23 16:17:55 -080061 auto status = aos::starter::GetStatus(application_name, config);
milind upadhyaya87957a2021-03-06 20:46:30 -080062 PrintKey();
63 PrintApplicationStatus(&status.message(), aos::monotonic_clock::now());
Philipp Schrader08537492021-01-23 16:17:55 -080064 } else {
65 LOG(ERROR) << "The \"status\" command requires zero or one arguments.";
66 return true;
67 }
68 return false;
69}
Tyler Chatowa79419d2020-08-12 20:12:11 -070070
Philipp Schrader08537492021-01-23 16:17:55 -080071bool InteractWithProgram(int argc, char **argv,
72 const aos::Configuration *config) {
73 const char *command_string = argv[0];
Philipp Schrader08537492021-01-23 16:17:55 -080074 if (argc != 2) {
75 LOG(ERROR) << "The \"" << command_string
76 << "\" command requires an application name as an argument.";
77 return true;
Tyler Chatowa79419d2020-08-12 20:12:11 -070078 }
79
Philipp Schrader08537492021-01-23 16:17:55 -080080 const auto command_search = kCommandConversions.find(command_string);
81 CHECK(command_search != kCommandConversions.end())
82 << "Internal error: \"" << command_string
83 << "\" is not in kCommandConversions.";
Tyler Chatowa79419d2020-08-12 20:12:11 -070084
Philipp Schrader08537492021-01-23 16:17:55 -080085 const aos::starter::Command command = command_search->second;
milind upadhyay4272f382021-04-07 18:03:08 -070086 const auto application_name = aos::starter::FindApplication(argv[1], config);
Philipp Schrader08537492021-01-23 16:17:55 -080087 if (aos::starter::SendCommandBlocking(command, application_name, config,
milind upadhyaya87957a2021-03-06 20:46:30 -080088 chrono::seconds(3))) {
Tyler Chatowa79419d2020-08-12 20:12:11 -070089 switch (command) {
90 case aos::starter::Command::START:
91 std::cout << "Successfully started " << application_name << '\n';
92 break;
93 case aos::starter::Command::STOP:
94 std::cout << "Successfully stopped " << application_name << '\n';
95 break;
96 case aos::starter::Command::RESTART:
97 std::cout << "Successfully restarted " << application_name << '\n';
98 break;
99 }
100 } else {
Philipp Schrader08537492021-01-23 16:17:55 -0800101 std::cout << "Failed to " << command_string << ' ' << application_name
102 << '\n';
Tyler Chatowa79419d2020-08-12 20:12:11 -0700103 }
Philipp Schrader08537492021-01-23 16:17:55 -0800104 return false;
105}
106
107// This is the set of subcommands we support. Each subcommand accepts argc and
108// argv from its own point of view. So argv[0] is always the name of the
109// subcommand. argv[1] and up are the arguments to the subcommand.
110// The subcommand returns true if there was an error parsing the command line
111// arguments. It returns false when the command line arguments are parsed
112// successfully.
113static const std::unordered_map<
114 std::string, std::function<bool(int argc, char **argv,
115 const aos::Configuration *config)>>
116 kCommands{
117 {"status", GetStarterStatus},
118 {"start", InteractWithProgram},
119 {"stop", InteractWithProgram},
120 {"restart", InteractWithProgram},
121 };
122
123} // namespace
124
125int main(int argc, char **argv) {
126 aos::InitGoogle(&argc, &argv);
127
128 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
129 aos::configuration::ReadConfig(FLAGS_config);
130
131 bool parsing_failed = false;
132
133 if (argc < 2) {
134 parsing_failed = true;
135 } else {
136 const char *command = argv[1];
137 auto it = kCommands.find(command);
138 if (it == kCommands.end()) {
139 parsing_failed = true;
140 } else {
141 parsing_failed = it->second(argc - 1, argv + 1, &config.message());
142 }
143 }
144
145 if (parsing_failed) {
146 LOG(ERROR) << "Parsing failed. Valid commands are:";
milind upadhyay4272f382021-04-07 18:03:08 -0700147 for (auto entry : kCommands) {
Philipp Schrader08537492021-01-23 16:17:55 -0800148 LOG(ERROR) << " - " << entry.first;
149 }
150 return 1;
151 }
152
153 return 0;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700154}