#include "bus-error.h"
#include "bus-common-errors.h"
#include "mkdir.h"
+#include "dropin.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
assert(unit_path);
STRV_FOREACH(p, lp->unit_path) {
- char *path;
+ _cleanup_free_ char *path;
path = path_join(arg_root, *p, unit_name);
if (!path)
if (access(path, F_OK) == 0) {
*unit_path = path;
+ path = NULL;
return 1;
}
-
- free(path);
}
return 0;
}
-static int unit_find_path(sd_bus *bus, const char *unit_name, const char *template, bool avoid_bus_cache, LookupPaths *lp, char **path) {
+static int unit_find_paths(sd_bus *bus,
+ const char *unit_name,
+ bool avoid_bus_cache,
+ LookupPaths *lp,
+ char **fragment_path,
+ char ***dropin_paths) {
int r;
+ /**
+ * Finds where the unit is defined on disk. Returns 0 if the unit
+ * is not found. Returns 1 if it is found, and sets
+ * - the path to the unit in *path, if it exists on disk,
+ * - and a strv of existing drop-ins in *dropins,
+ * if the arg is not NULL and any dropins were found.
+ */
+
assert(unit_name);
- assert(path);
+ assert(fragment_path);
assert(lp);
if (!avoid_bus_cache && !unit_name_is_template(unit_name)) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *unit = NULL;
- _cleanup_free_ char *tmp_path = NULL;
+ _cleanup_free_ char *path = NULL;
+ _cleanup_strv_free_ char **dropins = NULL;
unit = unit_dbus_path_from_name(unit_name);
if (!unit)
return log_oom();
- if (need_daemon_reload(bus, unit_name) > 0) {
+ if (need_daemon_reload(bus, unit_name) > 0)
warn_unit_file_changed(unit_name);
- return 0;
- }
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1.Unit",
"FragmentPath",
&error,
- &tmp_path);
- if (r < 0) {
- log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
- return 0;
+ &path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r));
+
+ r = sd_bus_get_property_strv(
+ bus,
+ "org.freedesktop.systemd1",
+ unit,
+ "org.freedesktop.systemd1.Unit",
+ "DropInPaths",
+ &error,
+ &dropins);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r));
+
+ r = 0;
+ if (!isempty(path)) {
+ *fragment_path = path;
+ path = NULL;
+ r = 1;
}
- if (isempty(tmp_path)) {
- log_warning("%s ignored: not found", template);
- return 0;
+ if (dropin_paths && !strv_isempty(dropins)) {
+ *dropin_paths = dropins;
+ dropins = NULL;
+ r = 1;
}
+ } else {
+ _cleanup_set_free_ Set *names;
- *path = tmp_path;
- tmp_path = NULL;
+ names = set_new(NULL);
+ if (!names)
+ return -ENOMEM;
- return 1;
- } else {
- r = unit_file_find_path(lp, template, path);
- if (r == 0)
- log_warning("%s ignored: not found", template);
- return r;
+ r = set_put(names, unit_name);
+ if (r < 0)
+ return r;
+
+ r = unit_file_find_path(lp, unit_name, fragment_path);
+ if (r < 0)
+ return r;
+
+ if (r == 0) {
+ _cleanup_free_ char *template;
+
+ template = unit_name_template(unit_name);
+ if (!template)
+ return log_oom();
+
+ if (!streq(template, unit_name)) {
+ r = unit_file_find_path(lp, template, fragment_path);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (dropin_paths)
+ r = unit_file_find_dropin_paths(lp->unit_path, NULL, names, dropin_paths);
}
- return 0;
+ return r;
}
typedef struct WaitData {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
+ if (!bus)
+ return log_error_errno(ENOTSUP, "Unit name globbing without bus is not implemented.");
+
r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
if (r < 0)
return r;
return ret;
}
+static int init_home_and_lookup_paths(char **user_home, char **user_runtime, LookupPaths *lp) {
+ int r;
+
+ assert(user_home);
+ assert(user_runtime);
+ assert(lp);
+
+ if (arg_scope == UNIT_FILE_USER) {
+ r = user_config_home(user_home);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
+ else if (r == 0)
+ return log_error_errno(ENOTDIR, "Cannot find units: $XDG_CONFIG_HOME and $HOME are not set.");
+
+ r = user_runtime_dir(user_runtime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
+ else if (r == 0)
+ return log_error_errno(ENOTDIR, "Cannot find units: $XDG_RUNTIME_DIR is not set.");
+ }
+
+ r = lookup_paths_init(lp,
+ arg_scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
+ arg_scope == UNIT_FILE_USER,
+ arg_root,
+ NULL, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to lookup unit lookup paths: %m");
+
+ return 0;
+}
+
static int cat(sd_bus *bus, char **args) {
+ _cleanup_free_ char *user_home = NULL;
+ _cleanup_free_ char *user_runtime = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
char **name;
- bool first = true;
+ bool first = true, avoid_bus_cache;
int r = 0;
- assert(bus);
assert(args);
+ r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
+ if (r < 0)
+ return r;
+
r = expand_names(bus, args + 1, NULL, &names);
if (r < 0)
log_error_errno(r, "Failed to expand names: %m");
+ avoid_bus_cache = !bus || avoid_bus();
+
pager_open_if_enabled();
STRV_FOREACH(name, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *fragment_path = NULL;
_cleanup_strv_free_ char **dropin_paths = NULL;
- _cleanup_free_ char *fragment_path = NULL, *unit = NULL;
char **path;
- unit = unit_dbus_path_from_name(*name);
- if (!unit)
- return log_oom();
-
- if (need_daemon_reload(bus, *name) > 0)
- warn_unit_file_changed(*name);
-
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- unit,
- "org.freedesktop.systemd1.Unit",
- "FragmentPath",
- &error,
- &fragment_path);
- if (r < 0) {
- log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
- continue;
- }
-
- r = sd_bus_get_property_strv(
- bus,
- "org.freedesktop.systemd1",
- unit,
- "org.freedesktop.systemd1.Unit",
- "DropInPaths",
- &error,
- &dropin_paths);
- if (r < 0) {
- log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
+ r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &fragment_path, &dropin_paths);
+ if (r < 0)
+ return r;
+ else if (r == 0) {
+ log_warning("Unit %s does not have any files on disk", *name);
continue;
}
else
puts("");
- if (!isempty(fragment_path)) {
+ if (fragment_path) {
printf("%s# %s%s\n",
ansi_highlight_blue(),
fragment_path,
return 0;
}
-static int get_drop_in_to_edit(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_path) {
- char *tmp_new_path;
- char *tmp;
-
- assert(unit_name);
- assert(ret_path);
+static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) {
+ _cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL;
switch (arg_scope) {
case UNIT_FILE_SYSTEM:
- tmp = strappenda(arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf");
+ path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name);
+ if (arg_runtime)
+ run = path_join(arg_root, "/run/systemd/system/", name);
break;
case UNIT_FILE_GLOBAL:
- tmp = strappenda(arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf");
+ path = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
+ if (arg_runtime)
+ run = path_join(arg_root, "/run/systemd/user/", name);
break;
case UNIT_FILE_USER:
assert(user_home);
assert(user_runtime);
- tmp = strappenda(arg_runtime ? user_runtime : user_home, "/", unit_name, ".d/override.conf");
+ path = path_join(arg_root, user_home, name);
+ if (arg_runtime) {
+ path2 = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
+ if (!path2)
+ return log_oom();
+ run = path_join(arg_root, user_runtime, name);
+ }
break;
default:
assert_not_reached("Invalid scope");
}
-
- tmp_new_path = path_join(arg_root, tmp, NULL);
- if (!tmp_new_path)
+ if (!path || (arg_runtime && !run))
return log_oom();
- *ret_path = tmp_new_path;
+ if (arg_runtime) {
+ if (access(path, F_OK) >= 0)
+ return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overriden by \"%s\" anyway.",
+ run, path);
+ if (path2 && access(path2, F_OK) >= 0)
+ return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overriden by \"%s\" anyway.",
+ run, path2);
+ *ret_path = run;
+ run = NULL;
+ } else {
+ *ret_path = path;
+ path = NULL;
+ }
return 0;
}
-static int unit_file_create_drop_in(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
- char *tmp_new_path;
+
+static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
+ char *tmp_new_path, *ending;
char *tmp_tmp_path;
int r;
assert(ret_new_path);
assert(ret_tmp_path);
- r = get_drop_in_to_edit(unit_name, user_home, user_runtime, &tmp_new_path);
+ ending = strappenda(unit_name, ".d/override.conf");
+ r = get_file_to_edit(ending, user_home, user_runtime, &tmp_new_path);
if (r < 0)
return r;
return 0;
}
-static bool unit_is_editable(const char *unit_name, const char *fragment_path, const char *user_home) {
- bool editable = true;
- const char *invalid_path;
-
- assert(unit_name);
-
- if (!arg_runtime)
- return true;
-
- switch (arg_scope) {
- case UNIT_FILE_SYSTEM:
- if (path_startswith(fragment_path, "/etc/systemd/system")) {
- editable = false;
- invalid_path = "/etc/systemd/system";
- } else if (path_startswith(fragment_path, SYSTEM_CONFIG_UNIT_PATH)) {
- editable = false;
- invalid_path = SYSTEM_CONFIG_UNIT_PATH;
- }
- break;
- case UNIT_FILE_GLOBAL:
- if (path_startswith(fragment_path, "/etc/systemd/user")) {
- editable = false;
- invalid_path = "/etc/systemd/user";
- } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) {
- editable = false;
- invalid_path = USER_CONFIG_UNIT_PATH;
- }
- break;
- case UNIT_FILE_USER:
- assert(user_home);
-
- if (path_startswith(fragment_path, "/etc/systemd/user")) {
- editable = false;
- invalid_path = "/etc/systemd/user";
- } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) {
- editable = false;
- invalid_path = USER_CONFIG_UNIT_PATH;
- } else if (path_startswith(fragment_path, user_home)) {
- editable = false;
- invalid_path = user_home;
- }
- break;
- default:
- assert_not_reached("Invalid scope");
- }
-
- if (!editable)
- log_error("%s ignored: cannot temporarily edit units from %s", unit_name, invalid_path);
-
- return editable;
-}
-
-static int get_copy_to_edit(const char *unit_name, const char *fragment_path, const char *user_home, const char *user_runtime, char **ret_path) {
- char *tmp_new_path;
-
- assert(unit_name);
- assert(ret_path);
-
- if (!unit_is_editable(unit_name, fragment_path, user_home))
- return -EINVAL;
-
- switch (arg_scope) {
- case UNIT_FILE_SYSTEM:
- tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH, unit_name);
- break;
- case UNIT_FILE_GLOBAL:
- tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH, unit_name);
- break;
- case UNIT_FILE_USER:
- assert(user_home);
- assert(user_runtime);
-
- tmp_new_path = path_join(arg_root, arg_runtime ? user_runtime : user_home, unit_name);
- break;
- default:
- assert_not_reached("Invalid scope");
- }
- if (!tmp_new_path)
- return log_oom();
-
- *ret_path = tmp_new_path;
-
- return 0;
-}
-
static int unit_file_create_copy(const char *unit_name,
const char *fragment_path,
const char *user_home,
assert(ret_new_path);
assert(ret_tmp_path);
- r = get_copy_to_edit(unit_name, fragment_path, user_home, user_runtime, &tmp_new_path);
+ r = get_file_to_edit(unit_name, user_home, user_runtime, &tmp_new_path);
if (r < 0)
return r;
}
}
- log_error("Cannot edit unit(s): No editor available. Please set either SYSTEMD_EDITOR or EDITOR or VISUAL environment variable");
+ log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR or $EDITOR or $VISUAL.");
_exit(EXIT_FAILURE);
}
assert(names);
assert(paths);
- if (arg_scope == UNIT_FILE_USER) {
- r = user_config_home(&user_home);
- if (r < 0)
- return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
- else if (r == 0)
- return log_error_errno(ENOTDIR, "Cannot edit units: $XDG_CONFIG_HOME and $HOME are not set.");
-
- r = user_runtime_dir(&user_runtime);
- if (r < 0)
- return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
- else if (r == 0)
- return log_error_errno(ENOTDIR, "Cannot edit units: $XDG_RUNTIME_DIR is not set.");
- }
-
- r = lookup_paths_init(&lp,
- arg_scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
- arg_scope == UNIT_FILE_USER,
- arg_root,
- NULL, NULL, NULL);
+ r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
if (r < 0)
- return log_error_errno(r, "Failed get lookup paths: %m");
+ return r;
avoid_bus_cache = !bus || avoid_bus();
STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL;
- _cleanup_free_ char *template = NULL;
char *new_path, *tmp_path;
- template = unit_name_template(*name);
- if (!template)
- return log_oom();
-
- r = unit_find_path(bus, *name, template, avoid_bus_cache, &lp, &path);
+ r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &path, NULL);
if (r < 0)
return r;
- else if (r == 0) {
- continue;
- }
+ else if (r == 0 || !path)
+ // FIXME: support units with path==NULL (no FragmentPath)
+ return log_error_errno(ENOENT, "Unit %s not found, cannot edit.", *name);
if (arg_full)
- r = unit_file_create_copy(template, path, user_home, user_runtime, &new_path, &tmp_path);
+ r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path);
else
- r = unit_file_create_drop_in(template, user_home, user_runtime, &new_path, &tmp_path);
-
+ r = unit_file_create_dropin(*name, user_home, user_runtime, &new_path, &tmp_path);
if (r < 0)
- continue;
+ return r;
r = strv_push_pair(paths, new_path, tmp_path);
if (r < 0)
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "HPrhkKt:afFc", options, NULL)) >= 0)
switch (c) {
case ARG_HELP:
case 't':
case 'a':
+ case 'f':
+ case 'F':
/* Compatibility nops */
break;
{ "check", MORE, 2, check_unit_active },
{ "is-failed", MORE, 2, check_unit_failed },
{ "show", MORE, 1, show },
- { "cat", MORE, 2, cat },
+ { "cat", MORE, 2, cat, NOBUS },
{ "status", MORE, 1, show },
{ "help", MORE, 2, show },
{ "snapshot", LESS, 2, snapshot },