Aktualizr
C++ SOTA Client
main.cc
1 #include <iostream>
2 
3 #include <boost/filesystem.hpp>
4 #include <boost/program_options.hpp>
5 #include <boost/property_tree/ini_parser.hpp>
6 #include <boost/signals2.hpp>
7 
8 #include "libaktualizr/aktualizr.h"
9 #include "libaktualizr/config.h"
10 #include "logging/logging.h"
11 #include "primary/aktualizr_helpers.h"
12 #include "secondary.h"
13 #include "utilities/aktualizr_version.h"
14 #include "utilities/sig_handler.h"
15 #include "utilities/utils.h"
16 
17 namespace bpo = boost::program_options;
18 
19 void checkInfoOptions(const bpo::options_description &description, const bpo::variables_map &vm) {
20  if (vm.count("help") != 0) {
21  std::cout << description << '\n';
22  exit(EXIT_SUCCESS);
23  }
24  if (vm.count("version") != 0) {
25  std::cout << "Current aktualizr version is: " << aktualizr_version() << "\n";
26  exit(EXIT_SUCCESS);
27  }
28 }
29 
30 bpo::variables_map parseOptions(int argc, char **argv) {
31  bpo::options_description description("aktualizr command line options");
32  // clang-format off
33  // Try to keep these options in the same order as Config::updateFromCommandLine().
34  // The first three are commandline only.
35  description.add_options()
36  ("help,h", "print usage")
37  ("version,v", "Current aktualizr version")
38  ("config,c", bpo::value<std::vector<boost::filesystem::path> >()->composing(), "configuration file or directory")
39  ("loglevel", bpo::value<int>(), "set log level 0-5 (trace, debug, info, warning, error, fatal)")
40  ("run-mode", bpo::value<std::string>(), "run mode of aktualizr: full, once, campaign_check, campaign_accept, campaign_decline, campaign_postpone, check, download, or install")
41  ("tls-server", bpo::value<std::string>(), "URL of device gateway")
42  ("repo-server", bpo::value<std::string>(), "URL of the Uptane Image repository")
43  ("director-server", bpo::value<std::string>(), "URL of the Uptane Director repository")
44  ("primary-ecu-serial", bpo::value<std::string>(), "serial number of Primary ECU")
45  ("primary-ecu-hardware-id", bpo::value<std::string>(), "hardware ID of Primary ECU")
46  ("secondary-config-file", bpo::value<boost::filesystem::path>(), "Secondary ECUs configuration file")
47  ("campaign-id", bpo::value<std::string>(), "ID of the campaign to act on")
48  ("hwinfo-file", bpo::value<boost::filesystem::path>(), "custom hardware information JSON file");
49  // clang-format on
50 
51  // consider the first positional argument as the aktualizr run mode
52  bpo::positional_options_description pos;
53  pos.add("run-mode", 1);
54 
55  bpo::variables_map vm;
56  std::vector<std::string> unregistered_options;
57  try {
58  bpo::basic_parsed_options<char> parsed_options =
59  bpo::command_line_parser(argc, argv).options(description).positional(pos).allow_unregistered().run();
60  bpo::store(parsed_options, vm);
61  checkInfoOptions(description, vm);
62  bpo::notify(vm);
63  unregistered_options = bpo::collect_unrecognized(parsed_options.options, bpo::exclude_positional);
64  if (vm.count("help") == 0 && !unregistered_options.empty()) {
65  std::cout << description << "\n";
66  exit(EXIT_FAILURE);
67  }
68  } catch (const bpo::required_option &ex) {
69  // print the error and append the default commandline option description
70  std::cout << ex.what() << std::endl << description;
71  exit(EXIT_FAILURE);
72  } catch (const bpo::error &ex) {
73  checkInfoOptions(description, vm);
74 
75  // log boost error
76  LOG_ERROR << "boost command line option error: " << ex.what();
77 
78  // print the error message to the standard output too, as the user provided
79  // a non-supported commandline option
80  std::cout << ex.what() << '\n';
81 
82  // set the returnValue, thereby ctest will recognize
83  // that something went wrong
84  exit(EXIT_FAILURE);
85  }
86 
87  return vm;
88 }
89 
90 void processEvent(const std::shared_ptr<event::BaseEvent> &event) {
91  if (event->isTypeOf<event::DownloadProgressReport>() || event->variant == "UpdateCheckComplete") {
92  // Do nothing; libaktualizr already logs it.
93  } else if (event->variant == "AllDownloadsComplete") {
94  const auto *downloads_complete = dynamic_cast<event::AllDownloadsComplete *>(event.get());
95  LOG_INFO << "got " << event->variant << " event with status: " << downloads_complete->result.status;
96  } else if (event->variant == "AllInstallsComplete") {
97  const auto *installs_complete = dynamic_cast<event::AllInstallsComplete *>(event.get());
98  LOG_INFO << "got " << event->variant << " event with status: " << installs_complete->result.dev_report.result_code;
99  } else {
100  LOG_INFO << "got " << event->variant << " event";
101  }
102 }
103 
104 int main(int argc, char *argv[]) {
105  logger_init();
106  logger_set_threshold(boost::log::trivial::info);
107 
108  bpo::variables_map commandline_map = parseOptions(argc, argv);
109 
110  LOG_INFO << "Aktualizr version " << aktualizr_version() << " starting";
111 
112  int r = EXIT_FAILURE;
113 
114  try {
115  if (geteuid() != 0) {
116  LOG_WARNING << "\033[31mAktualizr is currently running as non-root and may not work as expected! Aktualizr "
117  "should be run as root for proper functionality.\033[0m\n";
118  }
119  Config config(commandline_map);
120  LOG_DEBUG << "Current directory: " << boost::filesystem::current_path().string();
121 
122  Aktualizr aktualizr(config);
123  std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = processEvent;
124  boost::signals2::scoped_connection conn;
125 
126  conn = aktualizr.SetSignalHandler(f_cb);
127 
128  if (!config.uptane.secondary_config_file.empty()) {
129  try {
130  Primary::initSecondaries(aktualizr, config.uptane.secondary_config_file);
131  } catch (const std::exception &e) {
132  LOG_ERROR << "Failed to initialize Secondaries: " << e.what();
133  LOG_ERROR << "Exiting...";
134  return EXIT_FAILURE;
135  }
136  }
137 
138  aktualizr.Initialize();
139 
140  // handle unix signals
141  SigHandler::get().start([&aktualizr]() {
142  aktualizr.Abort();
143  aktualizr.Shutdown();
144  });
145  SigHandler::signal(SIGHUP);
146  SigHandler::signal(SIGINT);
147  SigHandler::signal(SIGTERM);
148 
149  Json::Value hwinfo;
150  if (commandline_map.count("hwinfo-file") != 0) {
151  auto file = commandline_map["hwinfo-file"].as<boost::filesystem::path>();
152  hwinfo = Utils::parseJSONFile(file);
153  if (hwinfo.empty()) {
154  LOG_ERROR << file << " is not a valid JSON file";
155  return EXIT_FAILURE;
156  }
157  }
158 
159  std::string run_mode;
160  if (commandline_map.count("run-mode") != 0) {
161  run_mode = commandline_map["run-mode"].as<std::string>();
162  }
163  // launch the first event
164  if (run_mode == "campaign_check") {
165  aktualizr.CampaignCheck().get();
166  } else if (run_mode == "campaign_accept" || run_mode == "campaign_decline" || run_mode == "campaign_postpone") {
167  if (commandline_map.count("campaign-id") == 0) {
168  throw std::runtime_error(run_mode + " requires a campaign ID");
169  }
170  aktualizr.CampaignControl(commandline_map["campaign-id"].as<std::string>(), campaign::cmdFromName(run_mode))
171  .get();
172  } else if (run_mode == "check") {
173  aktualizr.SendDeviceData(hwinfo).get();
174  aktualizr.CheckUpdates().get();
175  } else if (run_mode == "download") {
176  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
177  aktualizr.Download(update_result.updates).get();
178  } else if (run_mode == "install") {
179  result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
180  aktualizr.Install(update_result.updates).get();
181  } else if (run_mode == "once") {
182  aktualizr.SendDeviceData(hwinfo).get();
183  aktualizr.UptaneCycle();
184  } else {
185  boost::signals2::connection ac_conn =
186  aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1));
187 
188  try {
189  aktualizr.RunForever(hwinfo).get();
190  } catch (const std::exception &ex) {
191  LOG_ERROR << ex.what();
192  }
193 
194  LOG_DEBUG << "Aktualizr daemon exiting...";
195  }
196  r = EXIT_SUCCESS;
197  } catch (const std::exception &ex) {
198  LOG_ERROR << ex.what();
199  }
200 
201  return r;
202 }
event::AllDownloadsComplete
All targets for an update have been downloaded.
Definition: events.h:109
event::AllInstallsComplete
All ECU installation attempts for an update have completed.
Definition: events.h:148
result::UpdateCheck
Container for information about available updates.
Definition: results.h:37
Config
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:208
Aktualizr
This class provides the main APIs necessary for launching and controlling libaktualizr.
Definition: aktualizr.h:24
event::DownloadProgressReport
A report for a download in progress.
Definition: events.h:68
event
Aktualizr status events.
Definition: events.h:15