From fc41ebc7f3ffe47d87cd6ed289ced6293a07c6bc Mon Sep 17 00:00:00 2001 From: Dheyay Date: Tue, 5 Nov 2024 13:57:01 -0800 Subject: [PATCH] Update apt hook message format for oracular Fixes: LP #2086753 --- apt-hook/json-hook.cc | 168 +++++++++++++++++++++++++--------- apt-hook/json-hook.hh | 8 +- features/apt_messages.feature | 3 +- uaclient/apt_news.py | 18 +++- 4 files changed, 145 insertions(+), 52 deletions(-) diff --git a/apt-hook/json-hook.cc b/apt-hook/json-hook.cc index 45650c5961..34a05a564c 100644 --- a/apt-hook/json-hook.cc +++ b/apt-hook/json-hook.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -98,6 +99,32 @@ bool version_from_origin_and_archive_ends_with(json_object *version, std::string return false; } +release_info get_release_info() { + release_info info = {"", "", false}; + std::ifstream machine_series_file("/etc/os-release"); + if (machine_series_file.is_open()) { + std::string currentLine; + while(std::getline(machine_series_file, currentLine)) { + size_t position = currentLine.find("="); // Find position of equals sign as separator + std::string key = currentLine.substr(0, position); + std::string value = currentLine.substr(position + 1); + + if (!value.empty() && value.front() == '"' && value.back() == '"') { + value = value.substr(1, value.length() - 2); + } + + if (key=="VERSION_ID") { + info.release = value; + } else if (key == "VERSION_CODENAME") { + info.series = value; + } else if (key == "PRETTY_NAME" && value.find("LTS") != std::string::npos) { + info.is_lts = true; + } + } + } + return info; +} + bool count_security_packages_from_apt_stats_json(json_object *stats, security_package_counts &result) { bool has_key = false; result.standard = 0; @@ -193,7 +220,7 @@ bool collect_pro_packages_from_pre_prompt_json(json_object *pre_prompt, std::vec continue; } std::string package_mode(json_object_get_string(tmp)); - + has_key = json_object_object_get_ex(package, "name", &tmp); if (!has_key) { continue; @@ -234,21 +261,30 @@ bool collect_pro_packages_from_pre_prompt_json(json_object *pre_prompt, std::vec } #define MAX_COUNT_MESSAGE_LEN 256 -std::string create_count_message(security_package_counts &counts) { +std::string create_count_message(security_package_counts &counts, release_info info) { char buf[MAX_COUNT_MESSAGE_LEN] = {0}; + std::string lts_text = info.is_lts ? "LTS " : ""; if (counts.esm_apps == 0) { if (counts.esm_infra == 0) { if (counts.standard == 0) { return ""; } else if (counts.standard == 1) { - return std::string(gettext("1 standard LTS security update")); + // Don't want to do + lts_text. Instead use %s within the string. + std::snprintf( + buf, + MAX_COUNT_MESSAGE_LEN, + gettext("1 standard %ssecurity update"), + lts_text.c_str() + ); + return std::string(buf); } else if (counts.standard > 1) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates"), - counts.standard + gettext("%lu standard %ssecurity updates"), + counts.standard, + lts_text.c_str() ); return std::string(buf); } @@ -256,13 +292,20 @@ std::string create_count_message(security_package_counts &counts) { if (counts.standard == 0) { return std::string(gettext("1 esm-infra security update")); } else if (counts.standard == 1) { - return std::string(gettext("1 standard LTS security update and 1 esm-infra security update")); + std::snprintf( + buf, + MAX_COUNT_MESSAGE_LEN, + gettext("1 standard %ssecurity update and 1 esm-infra security update"), + lts_text.c_str() + ); + return std::string(buf); } else if (counts.standard > 1) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates and 1 esm-infra security update"), - counts.standard + gettext("%lu standard %ssecurity updates and 1 esm-infra security update"), + counts.standard, + lts_text.c_str() ); return std::string(buf); } @@ -279,7 +322,8 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("1 standard LTS security update and %lu esm-infra security updates"), + gettext("1 standard %ssecurity update and %lu esm-infra security updates"), + lts_text.c_str(), counts.esm_infra ); return std::string(buf); @@ -287,8 +331,9 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates and %lu esm-infra security updates"), + gettext("%lu standard %ssecurity updates and %lu esm-infra security updates"), counts.standard, + lts_text.c_str(), counts.esm_infra ); return std::string(buf); @@ -299,27 +344,46 @@ std::string create_count_message(security_package_counts &counts) { if (counts.standard == 0) { return std::string(gettext("1 esm-apps security update")); } else if (counts.standard == 1) { - return std::string(gettext("1 standard LTS security update and 1 esm-apps security update")); + std::snprintf( + buf, + MAX_COUNT_MESSAGE_LEN, + gettext("1 standard %ssecurity update and 1 esm-apps security update"), + lts_text.c_str() + ); + return std::string(buf); } else if (counts.standard > 1) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates and 1 esm-apps security update"), - counts.standard + gettext("%lu standard %ssecurity updates and 1 esm-apps security update"), + counts.standard, + lts_text.c_str() ); return std::string(buf); } } else if (counts.esm_infra == 1) { if (counts.standard == 0) { - return std::string(gettext("1 esm-infra security update and 1 esm-apps security update")); + std::snprintf( + buf, + MAX_COUNT_MESSAGE_LEN, + gettext("1 esm-infra security update and 1 esm-apps security update") + ); + return std::string(buf); } else if (counts.standard == 1) { - return std::string(gettext("1 standard LTS security update, 1 esm-infra security update and 1 esm-apps security update")); + std::snprintf( + buf, + MAX_COUNT_MESSAGE_LEN, + gettext("1 standard %ssecurity update, 1 esm-infra security update and 1 esm-apps security update"), + lts_text.c_str() + ); + return std::string(buf); } else if (counts.standard > 1) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates, 1 esm-infra security update and 1 esm-apps security update"), - counts.standard + gettext("%lu standard %ssecurity updates, 1 esm-infra security update and 1 esm-apps security update"), + counts.standard, + lts_text.c_str() ); return std::string(buf); } @@ -336,7 +400,8 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("1 standard LTS security update, %lu esm-infra security updates and 1 esm-apps security update"), + gettext("1 standard %ssecurity update, %lu esm-infra security updates and 1 esm-apps security update"), + lts_text.c_str(), counts.esm_infra ); return std::string(buf); @@ -344,8 +409,9 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates, %lu esm-infra security updates and 1 esm-apps security update"), + gettext("%lu standard %ssecurity updates, %lu esm-infra security updates and 1 esm-apps security update"), counts.standard, + lts_text.c_str(), counts.esm_infra ); return std::string(buf); @@ -365,7 +431,8 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("1 standard LTS security update and %lu esm-apps security updates"), + gettext("1 standard %ssecurity update and %lu esm-apps security updates"), + lts_text.c_str(), counts.esm_apps ); return std::string(buf); @@ -373,8 +440,9 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates and %lu esm-apps security updates"), + gettext("%lu standard %ssecurity updates and %lu esm-apps security updates"), counts.standard, + lts_text.c_str(), counts.esm_apps ); return std::string(buf); @@ -392,7 +460,8 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("1 standard LTS security update, 1 esm-infra security update and %lu esm-apps security updates"), + gettext("1 standard %ssecurity update, 1 esm-infra security update and %lu esm-apps security updates"), + lts_text.c_str(), counts.esm_apps ); return std::string(buf); @@ -400,8 +469,9 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates, 1 esm-infra security update and %lu esm-apps security updates"), + gettext("%lu standard %ssecurity updates, 1esm-infra security update and %lu esm-apps security updates"), counts.standard, + lts_text.c_str(), counts.esm_apps ); return std::string(buf); @@ -420,7 +490,8 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("1 standard LTS security update, %lu esm-infra security updates and %lu esm-apps security updates"), + gettext("1 standard %ssecurity update, %lu esm-infra security updates and %lu esm-apps security updates"), + lts_text.c_str(), counts.esm_infra, counts.esm_apps ); @@ -429,8 +500,9 @@ std::string create_count_message(security_package_counts &counts) { std::snprintf( buf, MAX_COUNT_MESSAGE_LEN, - gettext("%lu standard LTS security updates, %lu esm-infra security updates and %lu esm-apps security updates"), + gettext("%lu standard %ssecuriy updates, %lu esm-infra security updates and %lu esm-apps security updates"), counts.standard, + lts_text.c_str(), counts.esm_infra, counts.esm_apps ); @@ -463,24 +535,21 @@ CloudID get_cloud_id() { enum ESMInfraSeries {NOT_ESM_INFRA, XENIAL, BIONIC}; -ESMInfraSeries get_esm_infra_series() { - std::ifstream os_release_file("/etc/os-release"); +ESMInfraSeries get_esm_infra_series(release_info info) { ESMInfraSeries ret = NOT_ESM_INFRA; - if (os_release_file.is_open()) { - std::string os_release_str((std::istreambuf_iterator(os_release_file)), (std::istreambuf_iterator())); - if (os_release_str.find("xenial") != os_release_str.npos) { + if (info.release != "") { + if (info.series == "xenial") { ret = XENIAL; - } else if (os_release_str.find("bionic") != os_release_str.npos) { + } else if (info.series == "bionic") { ret = BIONIC; } - os_release_file.close(); } return ret; } -void print_learn_more_with_context() { +void print_learn_more_with_context(release_info info) { CloudID cloud_id = get_cloud_id(); - ESMInfraSeries esm_infra_series = get_esm_infra_series(); + ESMInfraSeries esm_infra_series = get_esm_infra_series(info); if (esm_infra_series == XENIAL) { if (cloud_id == AZURE) { @@ -563,8 +632,11 @@ void print_learn_more_with_context() { return; } -void print_package_names(std::vector package_names) { +void print_package_names(std::vector package_names, release_info info) { std::string curr_line = " "; + if (info.release != "" && info.release >= "24.10") { + curr_line = " "; + } for (std::string &name : package_names) { if ((curr_line.length() + 1 + name.length()) >= 79) { std::cout << curr_line << std::endl; @@ -577,7 +649,7 @@ void print_package_names(std::vector package_names) { } } -void print_esm_packages(ESMType esm_type, std::vector package_names) { +void print_esm_packages(ESMType esm_type, std::vector package_names, release_info info) { if (esm_type == APPS) { printf( ngettext( @@ -598,12 +670,12 @@ void print_esm_packages(ESMType esm_type, std::vector package_names printf("\n"); } - print_package_names(package_names); + print_package_names(package_names, info); - print_learn_more_with_context(); + print_learn_more_with_context(info); } -void print_expired_pro_packages(std::vector package_names) { +void print_expired_pro_packages(std::vector package_names, release_info info) { printf( gettext( "The following packages will fail to download because your Ubuntu Pro subscription has expired" @@ -611,7 +683,7 @@ void print_expired_pro_packages(std::vector package_names) { ); printf("\n"); - print_package_names(package_names); + print_package_names(package_names, info); printf( gettext( @@ -686,13 +758,19 @@ int run() std::cerr << "pro-hook: failed to read hook msg" << std::endl; return 0; } + release_info info = get_release_info(); if (hook_req.method == "org.debian.apt.hooks.install.statistics") { security_package_counts counts; success = count_security_packages_from_apt_stats_json(hook_req.params, counts); if (success) { - std::string message = create_count_message(counts); + std::string message = create_count_message(counts, info); if (message != "") { - std::cout << message << std::endl; + if (info.release != "" && info.release >= "24.10") { + std::cout << " " << message << std::endl; + } + else { + std::cout << message << std::endl; + } } } } else if (hook_req.method == "org.debian.apt.hooks.install.pre-prompt") { @@ -701,9 +779,9 @@ int run() success = get_potential_esm_updates(esm_updates); if (success) { if (!esm_updates.infra_packages.empty()) { - print_esm_packages(INFRA, esm_updates.infra_packages); + print_esm_packages(INFRA, esm_updates.infra_packages, info); } else if (!esm_updates.apps_packages.empty()) { - print_esm_packages(APPS, esm_updates.apps_packages); + print_esm_packages(APPS, esm_updates.apps_packages, info); } } @@ -720,7 +798,7 @@ int run() std::vector expired_packages; success = collect_pro_packages_from_pre_prompt_json(hook_req.params, &expired_packages); if (success && expired_packages.size() > 0) { - print_expired_pro_packages(expired_packages); + print_expired_pro_packages(expired_packages, info); } } } diff --git a/apt-hook/json-hook.hh b/apt-hook/json-hook.hh index b6ed5048b5..89bcb3da8d 100644 --- a/apt-hook/json-hook.hh +++ b/apt-hook/json-hook.hh @@ -13,12 +13,18 @@ struct security_package_counts { long unsigned int esm_infra; long unsigned int esm_apps; }; +struct release_info { + std::string release; // e.g. "24.04" + std::string series; // e.g. "noble" + bool is_lts; // true for LTS releases +}; enum ESMType {APPS, INFRA}; bool read_jsonrpc_request(std::istream &in, jsonrpc_request &req); +release_info get_release_info(); bool string_ends_with(std::string str, std::string ends_with); bool version_from_origin_and_archive_ends_with(json_object *version, std::string from_origin, std::string archive_ends_with); bool count_security_packages_from_apt_stats_json(json_object *stats, security_package_counts &result); -std::string create_count_message(security_package_counts &counts); +std::string create_count_message(security_package_counts &counts, release_info info); int run(); diff --git a/features/apt_messages.feature b/features/apt_messages.feature index cb18f96eaa..480329d03a 100644 --- a/features/apt_messages.feature +++ b/features/apt_messages.feature @@ -702,7 +702,8 @@ Feature: APT Messages Reading state information... Calculating upgrade... - one + APT news: + one Summary: Upgrading: 0, Installing: 0, Removing: 0, Not Upgrading: 0 diff --git a/uaclient/apt_news.py b/uaclient/apt_news.py index 68ea36e279..3b54d2bb11 100644 --- a/uaclient/apt_news.py +++ b/uaclient/apt_news.py @@ -263,11 +263,19 @@ def local_apt_news(cfg: UAConfig) -> Optional[str]: def format_news_for_apt_update(news: str) -> str: - prefix = "" if system.get_release_info().series == "oracular" else "#" - lines = [ - (prefix + " " + line) if prefix else line for line in news.split("\n") - ] - return "{0}\n{1}\n{0}\n".format(prefix, "\n".join(lines)) + lines = news.split("\n") + formatted_lines = [] + + if system.get_release_info().series == "oracular": + prefix = "\nAPT news:" + for line in lines: + formatted_lines.append(" " + line) + return "{0}\n{1}\n\n".format(prefix, "\n".join(formatted_lines)) + else: + prefix = "#" + for line in lines: + formatted_lines.append(prefix + " " + line) + return "{0}\n{1}\n{0}\n".format(prefix, "\n".join(formatted_lines)) def update_apt_news(cfg: UAConfig):