From: Lennart Poettering Date: Thu, 18 Jun 2026 16:02:37 +0000 (+0200) Subject: sysupdate: optionally load a per-component *.component file X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=291e6118f5912facb3d5343250ab5ffca3559840;p=thirdparty%2Fsystemd.git sysupdate: optionally load a per-component *.component file This file can be used for metadata about a component. It may also be used to "disable" a component. This brings components as a concept to a similar conceptual level as features: both can be enabled/disabled, and carry metadata --- diff --git a/src/sysupdate/sysupdate-util.c b/src/sysupdate/sysupdate-util.c index b3ed72577e9..8b0a4924e4c 100644 --- a/src/sysupdate/sysupdate-util.c +++ b/src/sysupdate/sysupdate-util.c @@ -37,6 +37,9 @@ bool component_name_valid(const char *c) { if (!string_is_safe(c, STRING_FILENAME_PART)) return false; + if (endswith(c, ".component")) /* don't allow ambiguity around sysupdate..component.d/ */ + return false; + /* Stack allocation is safe, since STRING_FILENAME_PART includes a length check */ const char *j = strjoina("sysupdate.", c, ".d"); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 2a277026b00..0704aabd9e3 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -10,6 +10,7 @@ #include "build.h" #include "bus-polkit.h" #include "conf-files.h" +#include "conf-parser.h" #include "constants.h" #include "discover-image.h" #include "dissect-image.h" @@ -38,6 +39,7 @@ #include "strv.h" #include "sysupdate.h" #include "sysupdate-cleanup.h" +#include "sysupdate-config.h" #include "sysupdate-feature.h" #include "sysupdate-instance.h" #include "sysupdate-target.h" @@ -75,6 +77,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_transfer_source, freep); #define CONTEXT_NULL \ (Context) { \ + .component_enabled = true, \ .sync = true, \ .instances_max = UINT64_MAX, \ .verify = -1, \ @@ -114,6 +117,8 @@ void context_done(Context *c) { c->root = mfree(c->root); c->image = mfree(c->image); c->component = mfree(c->component); + c->component_description = mfree(c->component_description); + c->component_documentation = strv_free(c->component_documentation); c->image_policy = image_policy_free(c->image_policy); c->transfer_source = mfree(c->transfer_source); @@ -280,9 +285,46 @@ static int read_transfers( return 0; } +static int read_component(Context *c) { + int r; + + assert(c); + + /* Read a component description file, but only if we actually operate on a component */ + if (c->definitions || !c->component) + return 0; + + _cleanup_free_ char *j = strjoin("sysupdate.", c->component, ".component"); + if (!j) + return log_oom(); + + ConfigTableItem table[] = { + { "Component", "Description", config_parse_string, 0, &c->component_description }, + { "Component", "Documentation", config_parse_url_specifiers_many, 0, &c->component_documentation }, + { "Component", "Enabled", config_parse_bool, 0, &c->component_enabled }, + {} + }; + + r = config_parse_standard_file_with_dropins_full( + c->root, + /* root_fd= */ -EBADF, + j, + "Component\0", + config_item_table_lookup, table, + CONFIG_PARSE_WARN, + /* userdata= */ (void *) c->root, + /* ret_stats_by_path= */ NULL, + /* ret_dropin_files= */ NULL); + if (r < 0) + return r; + + return 0; +} + typedef enum ReadDefinitionsFlags { - READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS = 1 << 0, - READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS = 1 << 1, + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS = 1 << 0, /* fail unless there's at least one enabled transfer */ + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS = 1 << 1, /* fail unless there's at least one transfer */ + READ_DEFINITIONS_REQUIRES_ENABLED_COMPONENT = 1 << 2, /* fail if component is disabled */ } ReadDefinitionsFlags; static int context_read_definitions(Context *c, const char* node, ReadDefinitionsFlags flags) { @@ -315,6 +357,10 @@ static int context_read_definitions(Context *c, const char* node, ReadDefinition if (!dirs) return log_oom(); + r = read_component(c); + if (r < 0) + return r; + r = read_features(c, (const char**) dirs); if (r < 0) return r; @@ -344,6 +390,9 @@ static int context_read_definitions(Context *c, const char* node, ReadDefinition "No transfer definitions found."); } + if (FLAGS_SET(flags, READ_DEFINITIONS_REQUIRES_ENABLED_COMPONENT) && !c->component_enabled) + return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "Component is disabled."); + return 0; } @@ -1094,7 +1143,9 @@ static int context_load_offline( static int context_load_online( Context *context, - ProcessImageFlags process_image_flags) { + ProcessImageFlags process_image_flags, + ReadDefinitionsFlags read_definitions_flags) { + int r; assert(context); @@ -1105,7 +1156,7 @@ static int context_load_online( r = context_load_offline( context, process_image_flags, - READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS|READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS); + read_definitions_flags); if (r < 0) return r; @@ -1156,7 +1207,10 @@ static int context_load_paths_from_image(Context *context, Image *image) { * syntactically validated by dispatch_target_identifier(), but might still point to components which don’t * exist, images which the user isn’t privileged to access, etc. This function validates the TargetIdentifier * against an enumerated list of known targets, which are safe to update without additional permissions. */ -static int context_load_online_from_target(Context *context, ProcessImageFlags process_image_flags) { +static int context_load_online_from_target( + Context *context, + ProcessImageFlags process_image_flags, + ReadDefinitionsFlags read_definitions_flags) { int r; assert(context); @@ -1247,7 +1301,7 @@ static int context_load_online_from_target(Context *context, ProcessImageFlags p assert_not_reached(); } - return context_load_online(context, process_image_flags); + return context_load_online(context, process_image_flags, read_definitions_flags); } static int context_on_acquire_progress(const Transfer *t, const Instance *inst, unsigned percentage) { @@ -1650,7 +1704,11 @@ static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { if (context.component_all) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--component-all currently not supported for '%s'.", argv[0]); - r = context_load_online(&context, PROCESS_IMAGE_READ_ONLY); + r = context_load_online( + &context, + PROCESS_IMAGE_READ_ONLY, + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS); if (r < 0) return r; @@ -1860,7 +1918,9 @@ static int verb_check_new(int argc, char *argv[], uintptr_t _data, void *userdat r = context_load_online( &context, - PROCESS_IMAGE_READ_ONLY); + PROCESS_IMAGE_READ_ONLY, + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS); if (r < 0) return r; @@ -1919,7 +1979,11 @@ static int vl_method_check_new(sd_varlink *link, sd_json_variant *parameters, sd /* CheckNew is always online */ context.offline = false; - r = context_load_online_from_target(&context, PROCESS_IMAGE_READ_ONLY); + r = context_load_online_from_target( + &context, + PROCESS_IMAGE_READ_ONLY, + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS); if (r == -ENOENT) return sd_varlink_error(link, "io.systemd.SysUpdate.NoSuchTarget", NULL); if (r < 0) @@ -1976,7 +2040,10 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag r = context_load_online( &context, - /* process_image_flags= */ 0); + /* process_image_flags= */ 0, + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ENABLED_COMPONENT); if (r < 0) { if (r != -ENOENT) return r; @@ -2108,7 +2175,9 @@ static int verb_pending_or_reboot(int argc, char *argv[], uintptr_t _data, void r = context_load_offline( &context, /* process_image_flags= */ 0, - READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS|READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS); + READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS| + READ_DEFINITIONS_REQUIRES_ENABLED_COMPONENT); if (r < 0) return r; diff --git a/src/sysupdate/sysupdate.h b/src/sysupdate/sysupdate.h index 3dcaae527d1..6088bde669b 100644 --- a/src/sysupdate/sysupdate.h +++ b/src/sysupdate/sysupdate.h @@ -24,6 +24,10 @@ typedef struct Context { LoopDevice *loop_device; char *mounted_dir; + char *component_description; + char **component_documentation; + bool component_enabled; + Transfer **transfers; size_t n_transfers;