1 #include <boost/program_options.hpp>
9 #include "logging/logging.h"
10 #include "uptane_repo.h"
11 #include "utilities/aktualizr_version.h"
13 namespace po = boost::program_options;
15 KeyType parseKeyType(
const po::variables_map &vm) {
16 std::string key_type_arg = vm[
"keytype"].as<std::string>();
17 std::istringstream key_type_str{std::string(
"\"") + key_type_arg +
"\""};
19 key_type_str >> key_type;
23 void check_info_options(
const po::options_description &description,
const po::variables_map &vm) {
24 if (vm.count(
"help") != 0 || (vm.count(
"command") == 0 && vm.count(
"version") == 0)) {
25 std::cout << description <<
'\n';
28 if (vm.count(
"version") != 0) {
29 std::cout <<
"Current uptane-generator version is: " << aktualizr_version() <<
"\n";
34 int main(
int argc,
char **argv) {
35 po::options_description desc(
"uptane-generator command line options");
38 (
"help,h",
"print usage")
39 (
"version,v",
"Current uptane-generator version")
40 (
"command", po::value<std::string>(),
"generate: \tgenerate a new repository\n"
41 "adddelegation: \tadd a delegated role to the Image repo metadata\n"
42 "revokedelegation: \tremove delegated role from the Image repo metadata and all signed targets of this role\n"
43 "image: \tadd a target to the Image repo metadata\n"
44 "addtarget: \tprepare Director Targets metadata for a given device\n"
45 "signtargets: \tsign the staged Director Targets metadata\n"
46 "emptytargets: \tclear the staged Director Targets metadata\n"
47 "oldtargets: \tfill the staged Director Targets metadata with what is currently signed\n"
48 "sign: \tsign arbitrary metadata with repo keys\n"
49 "addcampaigns: \tgenerate campaigns json\n"
50 "refresh: \trefresh a metadata object (bump the version)")
51 (
"path", po::value<boost::filesystem::path>(),
"path to the repository")
52 (
"filename", po::value<boost::filesystem::path>(),
"path to the image")
53 (
"hwid", po::value<std::string>(),
"target hardware identifier")
54 (
"targetformat", po::value<std::string>(),
"format of target for 'image' command")
55 (
"targetcustom", po::value<boost::filesystem::path>(),
"path to custom JSON for 'image' command")
56 (
"serial", po::value<std::string>(),
"target ECU serial")
57 (
"expires", po::value<std::string>(),
"expiration time")
58 (
"keyname", po::value<std::string>(),
"name of key's role")
59 (
"repotype", po::value<std::string>(),
"director|image")
60 (
"correlationid", po::value<std::string>()->default_value(
""),
"correlation id")
61 (
"keytype", po::value<std::string>()->default_value(
"RSA2048"),
"Uptane key type")
62 (
"targetname", po::value<std::string>(),
"target's name (if different than filename)")
63 (
"targetsha256", po::value<std::string>(),
"target's SHA256 hash (for adding metadata without an actual file)")
64 (
"targetsha512", po::value<std::string>(),
"target's SHA512 hash (for adding metadata without an actual file)")
65 (
"targetlength", po::value<uint64_t>(),
"target's length (for adding metadata without an actual file)")
66 (
"dname", po::value<std::string>(),
"delegated role name")
67 (
"dterm", po::bool_switch(),
"if the created delegated role is terminating")
68 (
"dparent", po::value<std::string>()->default_value(
"targets"),
"delegated role parent name")
69 (
"dpattern", po::value<std::string>(),
"delegated file path pattern")
70 (
"url", po::value<std::string>(),
"custom download URL");
74 po::positional_options_description positionalOptions;
75 positionalOptions.add(
"command", 1);
76 positionalOptions.add(
"path", 1);
77 positionalOptions.add(
"filename", 1);
81 logger_set_threshold(boost::log::trivial::info);
83 po::basic_parsed_options<char> parsed_options =
84 po::command_line_parser(argc, argv).options(desc).positional(positionalOptions).run();
85 po::store(parsed_options, vm);
86 check_info_options(desc, vm);
89 if (vm.count(
"command") != 0 && vm.count(
"path") != 0) {
90 std::string expiration_time;
91 if (vm.count(
"expires") != 0) {
92 expiration_time = vm[
"expires"].as<std::string>();
94 std::string correlation_id;
95 if (vm.count(
"correlationid") != 0) {
96 correlation_id = vm[
"correlationid"].as<std::string>();
99 if (vm.count(
"dname") != 0) {
100 dname = vm[
"dname"].as<std::string>();
102 std::string command = vm[
"command"].as<std::string>();
103 boost::filesystem::path repo_dir = vm[
"path"].as<boost::filesystem::path>();
104 UptaneRepo repo(repo_dir, expiration_time, correlation_id);
105 if (command ==
"generate") {
106 KeyType key_type = parseKeyType(vm);
107 repo.generateRepo(key_type);
108 std::cout <<
"Uptane metadata repos generated at " << repo_dir << std::endl;
109 }
else if (command ==
"image") {
110 if (vm.count(
"targetname") == 0 && vm.count(
"filename") == 0) {
111 std::cerr <<
"image command requires --targetname or --filename\n";
114 if (vm.count(
"hwid") == 0) {
115 std::cerr <<
"image command requires --hwid\n";
118 auto targetname = (vm.count(
"targetname") > 0) ? vm[
"targetname"].as<std::string>()
119 : vm[
"filename"].as<boost::filesystem::path>();
120 const std::string hwid = vm[
"hwid"].as<std::string>();
123 if (vm.count(
"dname") != 0) {
125 if (!delegation.isMatched(targetname)) {
126 std::cerr <<
"Image path doesn't match delegation!\n";
129 std::cout <<
"Added a target " << targetname <<
" to a delegated role " << dname << std::endl;
132 if (vm.count(
"url") != 0) {
133 url = vm[
"url"].as<std::string>();
135 if (vm.count(
"filename") > 0) {
136 repo.addImage(vm[
"filename"].as<boost::filesystem::path>(), targetname, hwid, url, delegation);
137 std::cout <<
"Added a target " << targetname <<
" to the Image repo metadata" << std::endl;
139 if ((vm.count(
"targetsha256") == 0 && vm.count(
"targetsha512") == 0) || vm.count(
"targetlength") == 0) {
140 std::cerr <<
"image command requires --targetsha256 or --targetsha512, and --targetlength when --filename "
141 "is not supplied.\n";
144 std::unique_ptr<Hash> hash;
145 if (vm.count(
"targetsha256") > 0) {
146 hash = std_::make_unique<Hash>(Hash::Type::kSha256, vm[
"targetsha256"].as<std::string>());
148 hash = std_::make_unique<Hash>(Hash::Type::kSha512, vm[
"targetsha512"].as<std::string>());
151 if (vm.count(
"targetcustom") > 0 && vm.count(
"targetformat") > 0) {
152 std::cerr <<
"--targetcustom and --targetformat cannot be used together";
155 if (vm.count(
"targetcustom") > 0) {
156 std::ifstream custom_file(vm[
"targetcustom"].as<boost::filesystem::path>().c_str());
157 custom_file >> custom;
158 }
else if (vm.count(
"targetformat") > 0) {
159 custom = Json::Value();
160 custom[
"targetFormat"] = vm[
"targetformat"].as<std::string>();
162 repo.addCustomImage(targetname.string(), *hash, vm[
"targetlength"].as<uint64_t>(), hwid, url, delegation,
164 std::cout <<
"Added a custom image target " << targetname.string() << std::endl;
166 }
else if (command ==
"addtarget") {
167 if (vm.count(
"targetname") == 0 || vm.count(
"hwid") == 0 || vm.count(
"serial") == 0) {
168 std::cerr <<
"addtarget command requires --targetname, --hwid, and --serial\n";
171 const std::string targetname = vm[
"targetname"].as<std::string>();
172 const std::string hwid = vm[
"hwid"].as<std::string>();
173 const std::string serial = vm[
"serial"].as<std::string>();
175 if (vm.count(
"url") != 0) {
176 url = vm[
"url"].as<std::string>();
178 repo.addTarget(targetname, hwid, serial, url, expiration_time);
179 std::cout <<
"Added target " << targetname <<
" to Director Targets metadata for ECU with serial " << serial
180 <<
" and hardware ID " << hwid << std::endl;
181 }
else if (command ==
"adddelegation") {
182 if (vm.count(
"dname") == 0 || vm.count(
"dpattern") == 0) {
183 std::cerr <<
"adddelegation command requires --dname and --dpattern\n";
186 std::string dparent = vm[
"dparent"].as<std::string>();
187 std::string dpattern = vm[
"dpattern"].as<std::string>();
188 KeyType key_type = parseKeyType(vm);
190 vm[
"dpattern"].as<std::string>(), vm[
"dterm"].as<bool>(), key_type);
191 std::cout <<
"Added a delegated role " << dname <<
" with dpattern " << dpattern
192 <<
" to the Image repo metadata" << std::endl;
193 }
else if (command ==
"revokedelegation") {
194 if (vm.count(
"dname") == 0) {
195 std::cerr <<
"revokedelegation command requires --dname\n";
199 std::cout <<
"Revoked the delegation " << dname << std::endl;
200 }
else if (command ==
"signtargets") {
202 std::cout <<
"Signed the staged Director Targets metadata" << std::endl;
203 }
else if (command ==
"emptytargets") {
205 std::cout <<
"Cleared the staged Director Targets metadata" << std::endl;
206 }
else if (command ==
"oldtargets") {
208 std::cout <<
"Populated the Director Targets metadata with the currently signed metadata" << std::endl;
209 }
else if (command ==
"sign") {
210 if (vm.count(
"repotype") == 0 || vm.count(
"keyname") == 0) {
211 std::cerr <<
"sign command requires --repotype and --keyname\n";
214 std::cin >> std::noskipws;
215 std::istream_iterator<char> it(std::cin);
216 std::istream_iterator<char> end;
217 std::string text_to_sign(it, end);
222 auto json_to_sign = Utils::parseJSON(text_to_sign);
223 if (json_to_sign.empty()) {
224 std::cerr <<
"Text to sign must be valid json\n";
228 auto json_signed = base_repo.signTuf(
Uptane::Role(vm[
"keyname"].as<std::string>()), json_to_sign);
229 std::cout << Utils::jsonToCanonicalStr(json_signed);
230 }
else if (command ==
"addcampaigns") {
231 repo.generateCampaigns();
232 std::cout <<
"Generated campaigns" << std::endl;
233 }
else if (command ==
"refresh") {
234 if (vm.count(
"repotype") == 0 || vm.count(
"keyname") == 0) {
235 std::cerr <<
"refresh command requires --repotype and --keyname\n";
241 std::cout << desc << std::endl;
245 std::cout << desc << std::endl;
251 }
catch (
const po::error &o) {
252 std::cout << o.what() << std::endl;
254 }
catch (std::exception &ex) {
255 LOG_ERROR <<
"Exception: " << ex.what();
257 LOG_ERROR <<
"Unknown error";