diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 6a532def968..7803cc9cf93 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -1544,6 +1544,15 @@ node /org/freedesktop/systemd1 { UTC. + + support-ended + + The system is running past the end of support declared by the vendor. See the + description of SUPPORT_END= in + os-release5. + + + old-kernel diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 880fb7e6bbf..96ba6832768 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -317,3 +317,38 @@ int load_extension_release_pairs(const char *root, const char *extension, char * return load_env_file_pairs(f, p, ret); } + +int os_release_support_ended(const char *support_end, bool quiet) { + _cleanup_free_ char *_support_end_alloc = NULL; + int r; + + if (!support_end) { + /* If the caller has the variably handy, they can pass it in. If not, we'll read it + * ourselves. */ + + r = parse_os_release(NULL, + "SUPPORT_END", &_support_end_alloc); + if (r < 0) + return log_full_errno((r == -ENOENT || quiet) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); + if (!_support_end_alloc) + return false; /* no end date defined */ + + support_end = _support_end_alloc; + } + + struct tm tm = {}; + + const char *k = strptime(support_end, "%Y-%m-%d", &tm); + if (!k || *k) + return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), + "Failed to parse SUPPORT_END= in os-release file, ignoring: %m"); + + time_t eol = mktime(&tm); + if (eol == (time_t) -1) + return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL), + "Failed to convert SUPPORT_END= in os-release file, ignoring: %m"); + + usec_t ts = now(CLOCK_REALTIME); + return DIV_ROUND_UP(ts, USEC_PER_SEC) > (usec_t) eol; +} diff --git a/src/basic/os-util.h b/src/basic/os-util.h index 271b35fa4ef..6883ceaf986 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -31,3 +31,5 @@ int _parse_os_release(const char *root, ...) _sentinel_; int load_extension_release_pairs(const char *root, const char *extension, char ***ret); int load_os_release_pairs(const char *root, char ***ret); int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret); + +int os_release_support_ended(const char *support_end, bool quiet); diff --git a/src/core/main.c b/src/core/main.c index 94c757e21e0..aee66051db8 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1312,29 +1312,46 @@ static int enforce_syscall_archs(Set *archs) { return 0; } -static int status_welcome(void) { - _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; +static int os_release_status(void) { + _cleanup_free_ char *pretty_name = NULL, *name = NULL, *version = NULL, + *ansi_color = NULL, *support_end = NULL; int r; - if (!show_status_on(arg_show_status)) - return 0; - r = parse_os_release(NULL, "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color); + "NAME", &name, + "VERSION", &version, + "ANSI_COLOR", &ansi_color, + "SUPPORT_END", &support_end); if (r < 0) - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "Failed to read os-release file, ignoring: %m"); + return log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); - if (log_get_show_color()) - return status_printf(NULL, 0, - "\nWelcome to \x1B[%sm%s\x1B[0m!\n", - isempty(ansi_color) ? "1" : ansi_color, - isempty(pretty_name) ? "Linux" : pretty_name); - else - return status_printf(NULL, 0, - "\nWelcome to %s!\n", - isempty(pretty_name) ? "Linux" : pretty_name); + const char *label = empty_to_null(pretty_name) ?: empty_to_null(name) ?: "Linux"; + + if (show_status_on(arg_show_status)) { + if (log_get_show_color()) + status_printf(NULL, 0, + "\nWelcome to \x1B[%sm%s\x1B[0m!\n", + empty_to_null(ansi_color) ?: "1", + label); + else + status_printf(NULL, 0, + "\nWelcome to %s!\n", + label); + } + + if (support_end && os_release_support_ended(support_end, false) > 0) + /* pretty_name may include the version already, so we'll print the version only if we + * have it and we're not using pretty_name. */ + status_printf(ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, 0, + "This OS version (%s%s%s) is past its end-of-support date (%s)", + label, + (pretty_name || !version) ? "" : " version ", + (pretty_name || !version) ? "" : version, + support_end); + + return 0; } static int write_container_id(void) { @@ -2100,7 +2117,7 @@ static int initialize_runtime( return r; } - status_welcome(); + (void) os_release_status(); (void) hostname_setup(true); /* Force transient machine-id on first boot. */ machine_id_setup(NULL, first_boot, arg_machine_id, NULL); diff --git a/src/core/manager.c b/src/core/manager.c index 9c0a686b2ab..c7598b1e2af 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -61,6 +61,7 @@ #include "manager-serialize.h" #include "memory-util.h" #include "mkdir-label.h" +#include "os-util.h" #include "parse-util.h" #include "path-lookup.h" #include "path-util.h" @@ -4475,7 +4476,7 @@ char* manager_taint_string(const Manager *m) { assert(m); - const char* stage[12] = {}; + const char* stage[13] = {}; size_t n = 0; if (m->taint_usr) @@ -4494,6 +4495,9 @@ char* manager_taint_string(const Manager *m) { if (clock_is_localtime(NULL) > 0) stage[n++] = "local-hwclock"; + if (os_release_support_ended(NULL, true) > 0) + stage[n++] = "support-ended"; + _cleanup_free_ char *destination = NULL; if (readlink_malloc("/var/run", &destination) < 0 || !PATH_IN_SET(destination, "../run", "/run")) diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c index 2cee6470c4a..0cfc1e3dbf9 100644 --- a/src/test/test-os-util.c +++ b/src/test/test-os-util.c @@ -72,4 +72,18 @@ TEST(load_os_release_pairs) { assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); } +TEST(os_release_support_ended) { + int r; + + assert_se(os_release_support_ended("1999-01-01", false) == true); + assert_se(os_release_support_ended("2037-12-31", false) == false); + assert_se(os_release_support_ended("-1-1-1", true) == -EINVAL); + + r = os_release_support_ended(NULL, false); + if (r < 0) + log_info_errno(r, "Failed to check host: %m"); + else + log_info_errno(r, "Host is supported: %s", yes_no(!r)); +} + DEFINE_TEST_MAIN(LOG_DEBUG);