1 #include "utilities/utils.h" 15 #include <archive_entry.h> 16 #include <arpa/inet.h> 19 #include <netinet/in.h> 21 #include <sys/types.h> 23 #include <boost/algorithm/string.hpp> 24 #include <boost/archive/iterators/base64_from_binary.hpp> 25 #include <boost/archive/iterators/binary_from_base64.hpp> 26 #include <boost/archive/iterators/remove_whitespace.hpp> 27 #include <boost/archive/iterators/transform_width.hpp> 28 #include <boost/filesystem.hpp> 29 #include <boost/uuid/random_generator.hpp> 30 #include <boost/uuid/uuid_io.hpp> 32 #include "logging/logging.h" 34 const char *adverbs[] = {
35 "adorable",
"acidic",
"ample",
"aromatic",
"artistic",
"attractive",
"basic",
"beautiful",
36 "best",
"blissful",
"bubbly",
"celebrated",
"cheap",
"chilly",
"cloudy",
"colorful",
37 "colossal",
"complete",
"conventional",
"costly",
"creamy",
"crisp",
"dense",
"double",
38 "dry",
"easy",
"every",
"exotic",
"expert",
"fake",
"fabulous",
"fast",
39 "fine",
"firm",
"first",
"flaky",
"flat",
"fluffy",
"frozen",
"generous",
40 "giant",
"glass",
"glorious",
"golden",
"good",
"grand",
"great",
"half",
41 "happy",
"hard",
"healthy",
"heavy",
"hot",
"huge",
"humble",
"ideal",
42 "icy",
"incredible",
"interesting",
"joyous",
"juicy",
"jumbo",
"large",
"late",
43 "lavish",
"leafy",
"lean",
"light",
"lovely",
"marvelous",
"mature",
"modern",
44 "modest",
"neat",
"new",
"nice",
"nifty",
"nutty",
"oily",
"ornate",
45 "perfect",
"plain",
"posh",
"pretty",
"prime",
"proper",
"pure",
"quick",
46 "raw",
"real",
"rich",
"ripe",
"safe",
"salty",
"several",
"short",
47 "simple",
"slim",
"slow",
"smooth",
"soft",
"solid",
"speedy",
"spotless",
48 "strong",
"stylish",
"subtle",
"super",
"sweet",
"tasty",
"tempting",
"tender",
49 "terrific",
"thick",
"thin",
"tidy",
"tiny",
"twin",
"ultimate",
"unique",
50 "uniform",
"unusual",
"valuable",
"vast",
"warm",
"wavy",
"wet",
"whole",
51 "wide",
"wild",
"wooden",
"young"};
53 const char *names[] = {
"Allerlei",
139 "Prinzregententorte",
161 "Sonnenblumenkernbrot",
182 typedef boost::archive::iterators::base64_from_binary<
183 boost::archive::iterators::transform_width<std::string::const_iterator, 6, 8> >
186 typedef boost::archive::iterators::transform_width<
187 boost::archive::iterators::binary_from_base64<
188 boost::archive::iterators::remove_whitespace<std::string::const_iterator> >,
192 std::string Utils::fromBase64(std::string base64_string) {
193 int64_t paddingChars = std::count(base64_string.begin(), base64_string.end(),
'=');
194 std::replace(base64_string.begin(), base64_string.end(),
'=',
'A');
195 std::string result(base64_to_bin(base64_string.begin()), base64_to_bin(base64_string.end()));
196 result.erase(result.end() - paddingChars, result.end());
200 std::string Utils::toBase64(
const std::string &tob64) {
201 std::string b64sig(base64_text(tob64.begin()), base64_text(tob64.end()));
202 b64sig.append((3 - tob64.length() % 3) % 3,
'=');
207 std::string Utils::stripQuotes(
const std::string &value) {
208 std::string res = value;
209 res.erase(std::remove(res.begin(), res.end(),
'\"'), res.end());
214 std::string Utils::addQuotes(
const std::string &value) {
return "\"" + value +
"\""; }
216 std::string Utils::extractField(
const std::string &in,
unsigned int field_id) {
218 auto it = in.begin();
221 for (; it != in.end() && (isspace(*it) != 0); it++) {
224 for (
unsigned int k = 0; k < field_id; k++) {
226 for (; it != in.end() && (isspace(*it) == 0); it++) {
230 throw std::runtime_error(
"No such field " + std::to_string(field_id));
232 for (; it != in.end() && (isspace(*it) != 0); it++) {
237 for (; it != in.end() && (isspace(*it) == 0); it++) {
243 Json::Value Utils::parseJSON(
const std::string &json_str) {
245 Json::Value json_value;
246 reader.parse(json_str, json_value);
250 Json::Value Utils::parseJSONFile(
const boost::filesystem::path &filename) {
251 std::ifstream path_stream(filename.c_str());
252 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
253 return Utils::parseJSON(content);
256 std::string Utils::genPrettyName() {
257 std::random_device urandom;
259 std::uniform_int_distribution<> adverbs_dist(0, (
sizeof(adverbs) /
sizeof(
char *)) - 1);
260 std::uniform_int_distribution<> nouns_dist(0, (
sizeof(names) /
sizeof(
char *)) - 1);
261 std::uniform_int_distribution<> digits(0, 999);
262 std::stringstream pretty_name;
263 pretty_name << adverbs[adverbs_dist(urandom)];
265 pretty_name << names[nouns_dist(urandom)];
267 pretty_name << digits(urandom);
268 std::string res = pretty_name.str();
269 std::transform(res.begin(), res.end(), res.begin(), ::tolower);
273 std::string Utils::readFile(
const boost::filesystem::path &filename) {
274 boost::filesystem::path tmpFilename = filename;
275 tmpFilename +=
".new";
277 if (boost::filesystem::exists(tmpFilename)) {
278 LOG_WARNING << tmpFilename <<
" was found on FS, removing";
279 boost::filesystem::remove(tmpFilename);
281 std::ifstream path_stream(filename.c_str());
282 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
286 static constexpr
size_t BSIZE = 20 * 512;
290 std::array<char, BSIZE> buf;
293 static ssize_t read_cb(
struct archive *a,
void *client_data,
const void **buffer) {
296 archive_set_error(a, -1,
"unable to read from stream");
302 s->is.read(s->buf.data(), BSIZE);
303 if (!s->is.eof() && s->is.fail()) {
304 archive_set_error(a, -1,
"unable to read from stream");
307 *buffer = s->buf.data();
309 return s->is.gcount();
312 void Utils::writeFile(
const boost::filesystem::path &filename,
const std::string &content,
bool create_directories) {
315 boost::filesystem::path tmpFilename = filename;
316 tmpFilename +=
".new";
318 if (create_directories) {
319 boost::filesystem::create_directories(filename.parent_path());
321 std::ofstream file(tmpFilename.c_str());
323 throw std::runtime_error(std::string(
"Error opening file ") + tmpFilename.string());
328 boost::filesystem::rename(tmpFilename, filename);
331 void Utils::writeFile(
const boost::filesystem::path &filename,
const Json::Value &content,
bool create_directories) {
332 Utils::writeFile(filename, jsonToStr(content), create_directories);
335 std::string Utils::jsonToStr(
const Json::Value &json) {
336 std::stringstream ss;
341 std::string Utils::jsonToCanonicalStr(
const Json::Value &json) {
return Json::FastWriter().write(json); }
343 Json::Value Utils::getHardwareInfo() {
345 const int exit_code = shell(
"lshw -json", &result);
347 if (exit_code != 0) {
348 LOG_WARNING <<
"Could not execute lshw (is it installed?).";
349 return Json::Value();
351 const Json::Value parsed = Utils::parseJSON(result);
352 return (parsed.isArray()) ? parsed[0] : parsed;
355 Json::Value Utils::getNetworkInfo() {
357 std::ifstream path_stream(
"/proc/net/route");
358 std::string route_content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
365 std::istringstream route_stream(route_content);
366 std::array<char, 200> line{};
369 route_stream.getline(&line[0], line.size());
370 while (route_stream.getline(&line[0], line.size())) {
371 std::string itfn = Utils::extractField(&line[0], 0);
372 std::string droute = Utils::extractField(&line[0], 1);
373 if (droute ==
"00000000") {
380 if (itf.name !=
"") {
383 StructGuard<struct ifaddrs> ifaddrs(
nullptr, freeifaddrs);
386 if (getifaddrs(&ifa) < 0) {
387 LOG_ERROR <<
"getifaddrs: " << std::strerror(errno);
392 if (ifaddrs !=
nullptr) {
393 for (
struct ifaddrs *ifa = ifaddrs.get(); ifa !=
nullptr; ifa = ifa->ifa_next) {
394 if (itf.name == ifa->ifa_name) {
395 if (ifa->ifa_addr ==
nullptr) {
398 if (ifa->ifa_addr->sa_family != AF_INET) {
401 const struct sockaddr_storage *sa =
reinterpret_cast<struct sockaddr_storage *
>(ifa->ifa_addr);
403 itf.ip = Utils::ipDisplayName(*sa);
410 std::ifstream mac_stream(
"/sys/class/net/" + itf.name +
"/address");
411 std::string m((std::istreambuf_iterator<char>(mac_stream)), std::istreambuf_iterator<char>());
412 itf.mac = std::move(m);
413 boost::trim_right(itf.mac);
417 Json::Value network_info;
418 network_info[
"local_ipv4"] = itf.ip;
419 network_info[
"mac"] = itf.mac;
420 network_info[
"hostname"] = Utils::getHostname();
425 std::string Utils::getHostname() {
427 if (gethostname(hostname, 200) < 0) {
433 std::string Utils::randomUuid() {
434 std::random_device urandom;
435 boost::uuids::basic_random_generator<std::random_device> uuid_gen(urandom);
436 return boost::uuids::to_string(uuid_gen());
439 void Utils::copyDir(
const boost::filesystem::path &from,
const boost::filesystem::path &to) {
440 boost::filesystem::remove_all(to);
442 boost::filesystem::create_directories(to);
443 boost::filesystem::directory_iterator it(from);
445 for (; it != boost::filesystem::directory_iterator(); it++) {
446 if (boost::filesystem::is_directory(it->path())) {
447 copyDir(it->path(), to / it->path().filename());
449 boost::filesystem::copy_file(it->path(), to / it->path().filename());
454 std::string Utils::readFileFromArchive(std::istream &as,
const std::string &filename) {
455 struct archive *a = archive_read_new();
457 LOG_ERROR <<
"archive error: could not initialize archive object";
458 throw std::runtime_error(
"archive error");
460 archive_read_support_filter_all(a);
461 archive_read_support_format_all(a);
463 int r = archive_read_open(a, reinterpret_cast<void *>(&state),
nullptr, read_cb,
nullptr);
464 if (r != ARCHIVE_OK) {
465 LOG_ERROR <<
"archive error: " << archive_error_string(a);
466 archive_read_free(a);
467 throw std::runtime_error(
"archive error");
471 std::stringstream out_stream;
472 struct archive_entry *entry;
473 while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
474 if (filename != archive_entry_pathname(entry)) {
475 archive_read_data_skip(a);
484 r = archive_read_data_block(a, reinterpret_cast<const void **>(&buff), &size, &offset);
485 if (r == ARCHIVE_EOF) {
489 if (r != ARCHIVE_OK) {
490 LOG_ERROR <<
"archive error: " << archive_error_string(a);
493 if (size > 0 && buff !=
nullptr) {
494 out_stream.write(buff, static_cast<ssize_t>(size));
499 r = archive_read_free(a);
500 if (r != ARCHIVE_OK) {
501 LOG_ERROR <<
"archive error: " << archive_error_string(a);
505 throw std::runtime_error(
"could not extract " + filename +
" from archive");
508 return out_stream.str();
511 static ssize_t write_cb(
struct archive *a,
void *client_data,
const void *buffer,
size_t length) {
512 auto s =
reinterpret_cast<std::ostream *
>(client_data);
513 s->write(reinterpret_cast<const char *>(buffer), static_cast<ssize_t>(length));
515 archive_set_error(a, -1,
"unable to write in stream");
519 return static_cast<ssize_t
>(length);
522 void Utils::writeArchive(
const std::map<std::string, std::string> &entries, std::ostream &as) {
523 struct archive *a = archive_write_new();
525 LOG_ERROR <<
"archive error: could not initialize archive object";
526 throw std::runtime_error(
"archive error");
528 archive_write_set_format_pax(a);
529 archive_write_add_filter_gzip(a);
531 int r = archive_write_open(a, reinterpret_cast<void *>(&as),
nullptr, write_cb,
nullptr);
532 if (r != ARCHIVE_OK) {
533 LOG_ERROR <<
"archive error: " << archive_error_string(a);
534 archive_write_free(a);
535 throw std::runtime_error(
"archive error");
538 struct archive_entry *entry = archive_entry_new();
539 for (
const auto &el : entries) {
540 archive_entry_clear(entry);
541 archive_entry_set_filetype(entry, AE_IFREG);
542 archive_entry_set_perm(entry, S_IRWXU | S_IRWXG | S_IRWXO);
543 archive_entry_set_size(entry, static_cast<ssize_t>(el.second.size()));
544 archive_entry_set_pathname(entry, el.first.c_str());
545 if (archive_write_header(a, entry) != 0) {
546 LOG_ERROR <<
"archive error: " << archive_error_string(a);
547 archive_entry_free(entry);
548 archive_write_free(a);
549 throw std::runtime_error(
"archive error");
551 if (archive_write_data(a, el.second.c_str(), el.second.size()) < 0) {
552 LOG_ERROR <<
"archive error: " << archive_error_string(a);
553 archive_entry_free(entry);
554 archive_write_free(a);
555 throw std::runtime_error(
"archive error");
558 archive_entry_free(entry);
559 r = archive_write_free(a);
560 if (r != ARCHIVE_OK) {
561 LOG_ERROR <<
"archive error: " << archive_error_string(a);
565 sockaddr_storage Utils::ipGetSockaddr(
int fd) {
566 sockaddr_storage ss{};
567 socklen_t len =
sizeof(ss);
568 if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) < 0) {
569 throw std::runtime_error(std::string(
"Could not get sockaddr: ") + std::strerror(errno));
575 std::string Utils::ipDisplayName(
const sockaddr_storage &saddr) {
576 char ipstr[INET6_ADDRSTRLEN];
578 switch (saddr.ss_family) {
580 const auto *sa =
reinterpret_cast<const sockaddr_in *
>(&saddr);
581 inet_ntop(AF_INET, &sa->sin_addr, ipstr,
sizeof(ipstr));
582 return std::string(ipstr);
585 const auto *sa =
reinterpret_cast<const sockaddr_in6 *
>(&saddr);
586 inet_ntop(AF_INET6, &sa->sin6_addr, ipstr,
sizeof(ipstr));
587 return std::string(ipstr);
594 int Utils::ipPort(
const sockaddr_storage &saddr) {
596 if (saddr.ss_family == AF_INET) {
597 const auto *sa =
reinterpret_cast<const sockaddr_in *
>(&saddr);
599 }
else if (saddr.ss_family == AF_INET6) {
600 const auto *sa =
reinterpret_cast<const sockaddr_in6 *
>(&saddr);
609 int Utils::shell(
const std::string &command, std::string *output,
bool include_stderr) {
611 std::string full_command(command);
612 if (include_stderr) {
613 full_command +=
" 2>&1";
615 FILE *pipe = popen(full_command.c_str(),
"r");
616 if (pipe ==
nullptr) {
617 *output =
"popen() failed!";
620 while (feof(pipe) == 0) {
621 if (fgets(buffer, 128, pipe) !=
nullptr) {
625 int exitcode = pclose(pipe);
626 return WEXITSTATUS(exitcode);
629 boost::filesystem::path Utils::absolutePath(
const boost::filesystem::path &root,
const boost::filesystem::path &file) {
630 if (file.is_absolute() || root.empty()) {
633 return (root / file);
636 std::vector<boost::filesystem::path> Utils::glob(
const std::string &pat) {
638 ::glob(pat.c_str(), GLOB_TILDE,
nullptr, &glob_result);
639 std::vector<boost::filesystem::path> ret;
640 for (
unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
642 ret.emplace_back(glob_result.gl_pathv[i]);
644 globfree(&glob_result);
645 std::sort(ret.begin(), ret.end());
649 void Utils::createDirectories(
const boost::filesystem::path &path, mode_t mode) {
650 boost::filesystem::path parent = path.parent_path();
651 if (!parent.empty() && !boost::filesystem::exists(parent)) {
652 Utils::createDirectories(parent, mode);
654 if (mkdir(path.c_str(), mode) == -1) {
655 throw std::runtime_error(
"could not create directory: " + path.native());
657 std::cout <<
"created: " << path.native() <<
"\n";
666 static boost::filesystem::path &Get() {
674 boost::filesystem::path p =
675 boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(
"aktualizr-%%%%-%%%%-%%%%-%%%%");
677 if (mkdir(p.c_str(), S_IRWXU) == -1) {
678 throw std::runtime_error(
"could not create temporary directory root: " + p.native());
681 path = boost::filesystem::path(p);
685 boost::filesystem::remove_all(path);
691 boost::filesystem::path path;
694 TemporaryFile::TemporaryFile(
const std::string &hint)
695 : tmp_name_(
SafeTempRoot::Get() /
boost::filesystem::unique_path(
"%%%%-%%%%-" + hint)) {}
697 TemporaryFile::~TemporaryFile() { boost::filesystem::remove(tmp_name_); }
699 void TemporaryFile::PutContents(
const std::string &contents) {
700 mode_t mode = S_IRUSR | S_IWUSR;
701 int fd = open(Path().c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
702 ssize_t written = write(fd, contents.c_str(), contents.size());
704 if (written < 0 || static_cast<size_t>(written) != contents.size()) {
705 throw std::runtime_error(std::string(
"Could not write content to file: ") + Path().
string());
709 boost::filesystem::path TemporaryFile::Path()
const {
return tmp_name_; }
711 std::string TemporaryFile::PathString()
const {
return Path().string(); }
713 TemporaryDirectory::TemporaryDirectory(
const std::string &hint)
714 : tmp_name_(
SafeTempRoot::Get() /
boost::filesystem::unique_path(
"%%%%-%%%%-" + hint)) {
715 Utils::createDirectories(tmp_name_, S_IRWXU);
718 TemporaryDirectory::~TemporaryDirectory() { boost::filesystem::remove_all(tmp_name_); }
720 boost::filesystem::path TemporaryDirectory::Path()
const {
return tmp_name_; }
722 boost::filesystem::path TemporaryDirectory::operator/(
const boost::filesystem::path &subdir)
const {
723 return (tmp_name_ / subdir);
726 std::string TemporaryDirectory::PathString()
const {
return Path().string(); }
728 void Utils::setSocketPort(sockaddr_storage *addr, in_port_t port) {
729 if (addr->ss_family == AF_INET) {
730 reinterpret_cast<sockaddr_in *
>(addr)->sin_port = port;
731 }
else if (addr->ss_family == AF_INET6) {
732 reinterpret_cast<sockaddr_in6 *
>(addr)->sin6_port = port;
736 bool operator<(
const sockaddr_storage &left,
const sockaddr_storage &right) {
737 if (left.ss_family == AF_INET) {
738 throw std::runtime_error(
"IPv4 addresses are not supported");
740 const unsigned char *left_addr =
reinterpret_cast<const sockaddr_in6 *
>(&left)->sin6_addr.s6_addr;
741 const unsigned char *right_addr = reinterpret_cast<const sockaddr_in6 *>(&right)->sin6_addr.s6_addr;
742 int res = memcmp(left_addr, right_addr, 16);