X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Finstall.c;h=61e572bdf3e99058d09fcfcaa92439283be1f91b;hp=caad2e55679f7f31aa4d64728655699b54eaa26d;hb=d9ab174bd7ec1dd5b382d3d84737d1c9ed1f4481;hpb=1f8c46040e824d7367c2f013de13c87d53f249dc diff --git a/src/shared/install.c b/src/shared/install.c index caad2e556..61e572bdf 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -45,7 +45,18 @@ typedef struct { Hashmap *have_installed; } InstallContext; -#define _cleanup_install_context_done_ _cleanup_(install_context_done) +static int in_search_path(const char *path, char **search) { + _cleanup_free_ char *parent = NULL; + int r; + + assert(path); + + r = path_get_parent(path, &parent); + if (r < 0) + return r; + + return strv_contains(search, parent); +} static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope, @@ -75,15 +86,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d case UNIT_FILE_SYSTEM: - if (root_dir && runtime) - asprintf(&p, "%s/run/systemd/system", root_dir); - else if (runtime) - p = strdup("/run/systemd/system"); - else if (root_dir) - asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); + if (runtime) + p = path_join(root_dir, "/run/systemd/system", NULL); else - p = strdup(SYSTEM_CONFIG_UNIT_PATH); - + p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL); break; case UNIT_FILE_GLOBAL: @@ -147,12 +153,16 @@ static int add_file_change( if (!c[i].path) return -ENOMEM; + path_kill_slashes(c[i].path); + if (source) { c[i].source = strdup(source); if (!c[i].source) { free(c[i].path); return -ENOMEM; } + + path_kill_slashes(c[i].path); } else c[i].source = NULL; @@ -169,7 +179,7 @@ static int mark_symlink_for_removal( assert(p); - r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func); + r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops); if (r < 0) return r; @@ -266,8 +276,22 @@ static int remove_marked_symlinks_fd( if (unit_name_is_instance(de->d_name) && instance_whitelist && - !strv_contains(instance_whitelist, de->d_name)) - continue; + !strv_contains(instance_whitelist, de->d_name)) { + + _cleanup_free_ char *w; + + /* OK, the file is not listed directly + * in the whitelist, so let's check if + * the template of it might be + * listed. */ + + w = unit_name_template(de->d_name); + if (!w) + return -ENOMEM; + + if (!strv_contains(instance_whitelist, w)) + continue; + } p = path_make_absolute(de->d_name, path); if (!p) @@ -290,18 +314,14 @@ static int remove_marked_symlinks_fd( if (!found) continue; - if (unlink(p) < 0 && errno != ENOENT) { - if (r == 0) r = -errno; continue; } - rmdir_parents(p, config_path); - path_kill_slashes(p); - + rmdir_parents(p, config_path); add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); if (!set_get(remove_symlinks_to, p)) { @@ -819,6 +839,7 @@ static void install_info_free(InstallInfo *i) { strv_free(i->aliases); strv_free(i->wanted_by); strv_free(i->required_by); + free(i->default_instance); free(i); } @@ -863,7 +884,7 @@ static int install_info_add( hashmap_get(c->will_install, name)) return 0; - r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func); + r = hashmap_ensure_allocated(&c->will_install, &string_hash_ops); if (r < 0) return r; @@ -911,31 +932,31 @@ static int install_info_add_auto( return install_info_add(c, name_or_path, NULL); } -static int config_parse_also(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char *w; +static int config_parse_also( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + size_t l; - char *state; + const char *word, *state; InstallContext *c = data; assert(filename); assert(lvalue); assert(rvalue); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { + FOREACH_WORD_QUOTED(word, l, rvalue, state) { _cleanup_free_ char *n; int r; - n = strndup(w, l); + n = strndup(word, l); if (!n) return -ENOMEM; @@ -943,23 +964,27 @@ static int config_parse_also(const char *unit, if (r < 0) return r; } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); return 0; } -static int config_parse_user(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int config_parse_user( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { InstallInfo *i = data; - char* printed; + char *printed; int r; assert(filename); @@ -976,29 +1001,68 @@ static int config_parse_user(const char *unit, return 0; } +static int config_parse_default_instance( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + InstallInfo *i = data; + char *printed; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = install_full_printf(i, rvalue, &printed); + if (r < 0) + return r; + + if (!unit_instance_is_valid(printed)) { + free(printed); + return -EINVAL; + } + + free(i->default_instance); + i->default_instance = printed; + + return 0; +} + static int unit_file_load( InstallContext *c, InstallInfo *info, const char *path, + const char *root_dir, bool allow_symlink) { const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_strv, 0, &info->aliases }, - { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, - { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, - { "Install", "Also", config_parse_also, 0, c }, - { "Exec", "User", config_parse_user, 0, info }, - { NULL, NULL, NULL, 0, NULL } + { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, + { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, + { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, + { "Install", "Also", config_parse_also, 0, c }, + { "Exec", "User", config_parse_user, 0, info }, + {} }; - int fd; _cleanup_fclose_ FILE *f = NULL; - int r; + int fd, r; assert(c); assert(info); assert(path); + if (!isempty(root_dir)) + path = strappenda(root_dir, "/", path); + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); if (fd < 0) return -errno; @@ -1009,8 +1073,10 @@ static int unit_file_load( return -ENOMEM; } - r = config_parse(NULL, path, f, NULL, - config_item_table_lookup, (void*) items, true, true, info); + r = config_parse(NULL, path, f, + NULL, + config_item_table_lookup, items, + true, true, false, info); if (r < 0) return r; @@ -1034,68 +1100,56 @@ static int unit_file_search( assert(info); assert(paths); - if (info->path) { - char *full_path = NULL; - - if (!isempty(root_dir)) - full_path = strappenda(root_dir, info->path); - - return unit_file_load(c, info, full_path ?: info->path, allow_symlink); - } + if (info->path) + return unit_file_load(c, info, info->path, root_dir, allow_symlink); assert(info->name); STRV_FOREACH(p, paths->unit_path) { - _cleanup_free_ char *path = NULL, *full_path = NULL; + _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", info->name, NULL); if (!path) return -ENOMEM; - if (!isempty(root_dir)) { - full_path = strappend(root_dir, path); - if (!full_path) - return -ENOMEM; - } - - r = unit_file_load(c, info, full_path ?: path, allow_symlink); + r = unit_file_load(c, info, path, root_dir, allow_symlink); if (r >= 0) { info->path = path; path = NULL; - } else if (r == -ENOENT && unit_name_is_instance(info->name)) { - /* Unit file doesn't exist, however instance enablement was requested. - * We will check if it is possible to load template unit file. */ - _cleanup_free_ char *template = NULL, *template_dir = NULL; + return r; + } + if (r != -ENOENT && r != -ELOOP) + return r; + } - template = unit_name_template(info->name); - if (!template) - return -ENOMEM; + if (unit_name_is_instance(info->name)) { + + /* Unit file doesn't exist, however instance + * enablement was requested. We will check if it is + * possible to load template unit file. */ - /* We will reuse path variable since we don't need it anymore. */ - template_dir = path; - *(strrchr(template_dir, '/') + 1) = '\0'; + _cleanup_free_ char *template = NULL; - path = strappend(template_dir, template); + template = unit_name_template(info->name); + if (!template) + return -ENOMEM; + + STRV_FOREACH(p, paths->unit_path) { + _cleanup_free_ char *path = NULL; + + path = strjoin(*p, "/", template, NULL); if (!path) return -ENOMEM; - if (!isempty(root_dir)) { - free(full_path); - full_path = strappend(root_dir, path); - if (!full_path) - return -ENOMEM; - } - - /* Let's try to load template unit. */ - r = unit_file_load(c, info, full_path ?: path, allow_symlink); + r = unit_file_load(c, info, path, root_dir, allow_symlink); if (r >= 0) { info->path = path; path = NULL; + return r; } + if (r != -ENOENT && r != -ELOOP) + return r; } - - if (r != -ENOENT && r != -ELOOP) - return r; } return -ENOENT; @@ -1107,7 +1161,7 @@ static int unit_file_can_install( const char *name, bool allow_symlink) { - _cleanup_install_context_done_ InstallContext c = {}; + _cleanup_(install_context_done) InstallContext c = {}; InstallInfo *i; int r; @@ -1209,54 +1263,37 @@ static int install_info_symlink_alias( static int install_info_symlink_wants( InstallInfo *i, const char *config_path, + char **list, + const char *suffix, bool force, UnitFileChange **changes, unsigned *n_changes) { + _cleanup_free_ char *buf = NULL; + const char *n; char **s; int r = 0, q; assert(i); assert(config_path); - STRV_FOREACH(s, i->wanted_by) { - _cleanup_free_ char *path = NULL, *dst = NULL; + if (unit_name_is_template(i->name)) { - q = install_full_printf(i, *s, &dst); - if (q < 0) - return q; + /* Don't install any symlink if there's no default + * instance configured */ - if (!unit_name_is_valid(dst, TEMPLATE_VALID)) { - r = -EINVAL; - continue; - } + if (!i->default_instance) + return 0; - if (asprintf(&path, "%s/%s.wants/%s", config_path, dst, i->name) < 0) + buf = unit_name_replace_instance(i->name, i->default_instance); + if (!buf) return -ENOMEM; - q = create_symlink(i->path, path, force, changes, n_changes); - - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_requires( - InstallInfo *i, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **s; - int r = 0, q; - - assert(i); - assert(config_path); + n = buf; + } else + n = i->name; - STRV_FOREACH(s, i->required_by) { + STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; q = install_full_printf(i, *s, &dst); @@ -1268,11 +1305,11 @@ static int install_info_symlink_requires( continue; } - if (asprintf(&path, "%s/%s.requires/%s", config_path, dst, i->name) < 0) + path = strjoin(config_path, "/", dst, suffix, n, NULL); + if (!path) return -ENOMEM; q = create_symlink(i->path, path, force, changes, n_changes); - if (r == 0) r = q; } @@ -1284,6 +1321,7 @@ static int install_info_symlink_link( InstallInfo *i, LookupPaths *paths, const char *config_path, + const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1311,6 +1349,7 @@ static int install_info_apply( InstallInfo *i, LookupPaths *paths, const char *config_path, + const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1323,15 +1362,15 @@ static int install_info_apply( r = install_info_symlink_alias(i, config_path, force, changes, n_changes); - q = install_info_symlink_wants(i, config_path, force, changes, n_changes); + q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_requires(i, config_path, force, changes, n_changes); + q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes); if (r == 0) r = q; @@ -1356,7 +1395,7 @@ static int install_context_apply( while ((i = hashmap_first(c->will_install))) { - q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + q = hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); if (q < 0) return q; @@ -1371,7 +1410,7 @@ static int install_context_apply( } else if (r >= 0) r += q; - q = install_info_apply(i, paths, config_path, force, changes, n_changes); + q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); if (r >= 0 && q < 0) r = q; } @@ -1397,7 +1436,7 @@ static int install_context_mark_for_removal( while ((i = hashmap_first(c->will_install))) { - q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + q = hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); if (q < 0) return q; @@ -1459,7 +1498,7 @@ int unit_file_enable( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_install_context_done_ InstallContext c = {}; + _cleanup_(install_context_done) InstallContext c = {}; char **i; _cleanup_free_ char *config_path = NULL; int r; @@ -1498,7 +1537,7 @@ int unit_file_disable( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_install_context_done_ InstallContext c = {}; + _cleanup_(install_context_done) InstallContext c = {}; char **i; _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; @@ -1558,7 +1597,7 @@ int unit_file_set_default( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_install_context_done_ InstallContext c = {}; + _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; char *path; int r; @@ -1619,11 +1658,7 @@ int unit_file_get_default( _cleanup_free_ char *path = NULL, *tmp = NULL; char *n; - if (isempty(root_dir)) - path = strappend(*p, "/" SPECIAL_DEFAULT_TARGET); - else - path = strjoin(root_dir, "/", *p, "/" SPECIAL_DEFAULT_TARGET, NULL); - + path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET); if (!path) return -ENOMEM; @@ -1680,15 +1715,12 @@ UnitFileState unit_file_get_state( free(path); path = NULL; - if (root_dir) - asprintf(&path, "%s/%s/%s", root_dir, *i, name); - else - asprintf(&path, "%s/%s", *i, name); + path = path_join(root_dir, *i, name); if (!path) return -ENOMEM; if (root_dir) - partial = path + strlen(root_dir) + 1; + partial = path + strlen(root_dir); else partial = path; @@ -1737,7 +1769,7 @@ UnitFileState unit_file_get_state( int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { _cleanup_strv_free_ char **files = NULL; - char **i; + char **p; int r; assert(scope >= 0); @@ -1765,17 +1797,10 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char if (r < 0) return r; - STRV_FOREACH(i, files) { - _cleanup_free_ char *buf = NULL; + STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; - const char *p; - - if (root_dir) - p = buf = strjoin(root_dir, "/", *i, NULL); - else - p = *i; - f = fopen(p, "re"); + f = fopen(*p, "re"); if (!f) { if (errno == ENOENT) continue; @@ -1834,7 +1859,7 @@ int unit_file_preset( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_install_context_done_ InstallContext plus = {}, minus = {}; + _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; @@ -1842,7 +1867,7 @@ int unit_file_preset( assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MODE_MAX); + assert(mode < _UNIT_FILE_PRESET_MAX); r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) @@ -1902,7 +1927,7 @@ int unit_file_preset_all( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_install_context_done_ InstallContext plus = {}, minus = {}; + _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; @@ -1910,7 +1935,7 @@ int unit_file_preset_all( assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MODE_MAX); + assert(mode < _UNIT_FILE_PRESET_MAX); r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) @@ -1922,17 +1947,11 @@ int unit_file_preset_all( STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *buf = NULL; - const char *units_dir; + _cleanup_free_ char *units_dir; - if (!isempty(root_dir)) { - buf = strjoin(root_dir, "/", *i, NULL); - if (!buf) - return -ENOMEM; - - units_dir = buf; - } else - units_dir = *i; + units_dir = path_join(root_dir, *i, NULL); + if (!units_dir) + return -ENOMEM; d = opendir(units_dir); if (!d) { @@ -2000,14 +2019,15 @@ int unit_file_preset_all( return r; } -static void unitfilelist_free(UnitFileList **f) { - if (!*f) +static void unit_file_list_free_one(UnitFileList *f) { + if (!f) return; - free((*f)->path); - free(*f); + free(f->path); + free(f); } -#define _cleanup_unitfilelist_free_ _cleanup_(unitfilelist_free) + +DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( UnitFileScope scope, @@ -2025,23 +2045,23 @@ int unit_file_get_list( if (root_dir && scope != UNIT_FILE_SYSTEM) return -EINVAL; + if (root_dir) { + r = access(root_dir, F_OK); + if (r < 0) + return -errno; + } + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *buf = NULL; - const char *units_dir; - - if (!isempty(root_dir)) { - buf = strjoin(root_dir, "/", *i, NULL); - if (!buf) - return -ENOMEM; + _cleanup_free_ char *units_dir; - units_dir = buf; - } else - units_dir = *i; + units_dir = path_join(root_dir, *i, NULL); + if (!units_dir) + return -ENOMEM; d = opendir(units_dir); if (!d) { @@ -2052,8 +2072,9 @@ int unit_file_get_list( } for (;;) { - _cleanup_unitfilelist_free_ UnitFileList *f = NULL; + _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; struct dirent *de; + _cleanup_free_ char *path = NULL; errno = 0; de = readdir(d); @@ -2103,7 +2124,11 @@ int unit_file_get_list( goto found; } - r = unit_file_can_install(&paths, root_dir, f->path, true); + path = path_make_absolute(de->d_name, *i); + if (!path) + return -ENOMEM; + + r = unit_file_can_install(&paths, root_dir, path, true); if (r == -EINVAL || /* Invalid setting? */ r == -EBADMSG || /* Invalid format? */ r == -ENOENT /* Included file not found? */) @@ -2147,7 +2172,7 @@ static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); -static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = { +static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { [UNIT_FILE_PRESET_FULL] = "full", [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",