void *data,
void *userdata) {
- const char *name;
UnitType type;
+ assert(unit);
assert(filename);
assert(lvalue);
assert(rvalue);
- name = basename(filename);
- type = unit_name_to_type(name);
+ type = unit_name_to_type(unit);
if (!unit_type_may_alias(type))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
"Alias= is not allowed for %s units, ignoring.",
InstallContext *c = data;
int r;
+ assert(unit);
assert(filename);
assert(lvalue);
assert(rvalue);
void *userdata) {
UnitFileInstallInfo *i = data;
- const char *name;
_cleanup_free_ char *printed = NULL;
int r;
+ assert(unit);
assert(filename);
assert(lvalue);
assert(rvalue);
- name = basename(filename);
- if (unit_name_is_valid(name, UNIT_NAME_INSTANCE))
+ if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
/* When enabling an instance, we might be using a template unit file,
* but we should ignore DefaultInstance silently. */
return 0;
- if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
+ if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
- "DefaultInstance= only makes sense for template units, ignoring.");
+ "DefaultInstance= only makes sense for template units, ignoring. %s", unit);
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
{}
};
- const char *name;
UnitType type;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
assert(info);
assert(path);
- name = basename(path);
- type = unit_name_to_type(name);
- if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
+ type = unit_name_to_type(info->name);
+ if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
!unit_type_may_template(type))
return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
return -errno;
fd = -1;
- r = config_parse(NULL, path, f,
+ r = config_parse(info->name, path, f,
NULL,
config_item_table_lookup, items,
true, true, false, info);
SearchFlags flags) {
_cleanup_free_ char *template = NULL;
+ _cleanup_strv_free_ char **dirs = NULL;
+ _cleanup_free_ char **files = NULL;
+ const char *dropin_dir_name = NULL;
+ const char *dropin_template_dir_name = NULL;
+
char **p;
int r;
+ int result;
+ bool found_unit = false;
assert(info);
assert(paths);
assert(info->name);
+ if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
+ r = unit_name_template(info->name, &template);
+ if (r < 0)
+ return r;
+ }
+
STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
return -ENOMEM;
r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
+
if (r >= 0) {
info->path = path;
path = NULL;
- return r;
+ result = r;
+ found_unit = true;
+ break;
} else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
return r;
}
- if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
+ if (!found_unit && template) {
+
/* Unit file doesn't exist, however instance
* enablement was requested. We will check if it is
* possible to load template unit file. */
- r = unit_name_template(info->name, &template);
- if (r < 0)
- return r;
-
STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
if (r >= 0) {
info->path = path;
path = NULL;
- return r;
+ result = r;
+ found_unit = true;
+ break;
} else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
return r;
}
}
- log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
- return -ENOENT;
+ if (!found_unit) {
+ log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
+ return -ENOENT;
+ }
+
+ /* Search for drop-in directories */
+
+ dropin_dir_name = strjoina(info->name, ".d");
+ STRV_FOREACH(p, paths->search_path) {
+ char *path;
+
+ path = path_join(NULL, *p, dropin_dir_name);
+ if (!path)
+ return -ENOMEM;
+
+ r = strv_consume(&dirs, path);
+ if (r < 0)
+ return r;
+ }
+
+ if (template) {
+ dropin_template_dir_name = strjoina(template, ".d");
+ STRV_FOREACH(p, paths->search_path) {
+ char *path;
+
+ path = path_join(NULL, *p, dropin_template_dir_name);
+ if (!path)
+ return -ENOMEM;
+
+ r = strv_consume(&dirs, path);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ /* Load drop-in conf files */
+
+ r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get list of conf files: %m");
+
+ STRV_FOREACH(p, files) {
+ r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
+ }
+
+ return result;
}
static int install_info_follow(
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC);
}
+static void test_with_dropin(const char *root) {
+ const char *p;
+ UnitFileState state;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-3.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4a.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-4a.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "Also=with-dropin-4b.service\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4b.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service"));
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-1.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-1.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service"));
+ assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-2.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service"));
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-3.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-3.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 1);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service"));
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-4a.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-4b.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+}
+
+static void test_with_dropin_template(const char *root) {
+ const char *p;
+ UnitFileState state;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@instance-1.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "DefaultInstance=instance-1\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "DefaultInstance=instance-2\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-1@instance-1.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-1@instance-1.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2@instance-1.service");
+ assert_se(streq(changes[0].path, p));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-2@instance-1.service");
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2@instance-2.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-3@instance-2.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
test_preset_order(root);
test_revert(root);
test_static_instance(root);
+ test_with_dropin(root);
+ test_with_dropin_template(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);