[systemd-devel] [PATCH v2] systemctl: add edit verb
Ronny Chevalier
chevalier.ronny at gmail.com
Sat Oct 18 09:12:43 PDT 2014
It helps editing units by either creating a drop-in file, like
/etc/systemd/system/my.service.d/amendments.conf, or by copying the
original unit from /usr/lib/systemd/ to /etc/systemd/ if the --full
option is specified. Then it invokes an editor to the related files
and daemon-reload is invoked when the editor exited successfully.
See https://bugzilla.redhat.com/show_bug.cgi?id=906824
---
TODO | 2 -
man/journalctl.xml | 6 +-
man/less-variables.xml | 40 ++-
man/localectl.xml | 6 +-
man/loginctl.xml | 6 +-
man/machinectl.xml | 6 +-
man/systemctl.xml | 49 +++-
man/systemd-analyze.xml | 6 +-
man/timedatectl.xml | 6 +-
src/systemctl/systemctl.c | 650 +++++++++++++++++++++++++++++++++-------------
10 files changed, 564 insertions(+), 213 deletions(-)
diff --git a/TODO b/TODO
index 3206420..cc8d8c4 100644
--- a/TODO
+++ b/TODO
@@ -66,8 +66,6 @@ Features:
* systemctl: if it fails, show log output?
-* maybe add "systemctl edit" that copies unit files from /usr/lib/systemd/system to /etc/systemd/system and invokes vim on them
-
* dbus: add new message hdr field for allowing interactive auth, write spec for it. update dbus spec to mandate that unknown flags *must* be ignored...
* maybe introduce AssertXYZ= similar to ConditionXYZ= that causes a unit to fail (instead of skipping it) if some condition is not true...
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 7fb6afc..d36889f 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -891,7 +891,11 @@
failure code is returned.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>Examples</title>
diff --git a/man/less-variables.xml b/man/less-variables.xml
index 09cbd42..1b8aae0 100644
--- a/man/less-variables.xml
+++ b/man/less-variables.xml
@@ -2,28 +2,24 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
-<refsect1>
- <title>Environment</title>
+<variablelist class='environment-variables'>
+ <varlistentry>
+ <term><varname>$SYSTEMD_PAGER</varname></term>
- <variablelist class='environment-variables'>
- <varlistentry>
- <term><varname>$SYSTEMD_PAGER</varname></term>
+ <listitem><para>Pager to use when
+ <option>--no-pager</option> is not given;
+ overrides <varname>$PAGER</varname>. Setting
+ this to an empty string or the value
+ <literal>cat</literal> is equivalent to passing
+ <option>--no-pager</option>.</para></listitem>
+ </varlistentry>
- <listitem><para>Pager to use when
- <option>--no-pager</option> is not given;
- overrides <varname>$PAGER</varname>. Setting
- this to an empty string or the value
- <literal>cat</literal> is equivalent to passing
- <option>--no-pager</option>.</para></listitem>
- </varlistentry>
+ <varlistentry>
+ <term><varname>$SYSTEMD_LESS</varname></term>
- <varlistentry>
- <term><varname>$SYSTEMD_LESS</varname></term>
-
- <listitem><para>Override the default
- options passed to
- <command>less</command>
- (<literal>FRSXMK</literal>).</para></listitem>
- </varlistentry>
- </variablelist>
-</refsect1>
+ <listitem><para>Override the default
+ options passed to
+ <command>less</command>
+ (<literal>FRSXMK</literal>).</para></listitem>
+ </varlistentry>
+</variablelist>
diff --git a/man/localectl.xml b/man/localectl.xml
index 38e73c7..7ae6c60 100644
--- a/man/localectl.xml
+++ b/man/localectl.xml
@@ -223,7 +223,11 @@
code otherwise.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/loginctl.xml b/man/loginctl.xml
index 749db92..4754790 100644
--- a/man/loginctl.xml
+++ b/man/loginctl.xml
@@ -438,7 +438,11 @@
code otherwise.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/machinectl.xml b/man/machinectl.xml
index 2f2e257..b95b7fe 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -281,7 +281,11 @@
code otherwise.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 61a23de..44dc7cb 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1150,6 +1150,31 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<filename>default.target</filename> to the given unit.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><command>edit <replaceable>NAME</replaceable>...</command></term>
+
+ <listitem>
+ <para>Edit one or more unit files, as specified on the command
+ line.</para>
+
+ <para>Depending on whether <option>--system</option> (the default),
+ <option>--user</option>, or <option>--global</option> is specified,
+ this create a drop-in file for each units either for the system,
+ for the calling user or for all futures logins of all users. Then
+ the editor is invoked on them (see section "Environment" below).</para>
+
+ <para>If <option>--full</option> is specified, this will copy the original
+ units instead of creating drop-in files.</para>
+
+ <para>After the units have been edited, the systemd configuration is
+ reloaded (in a way that is equivalent to <command>daemon-reload</command>),
+ but it does not restart or reload the units.</para>
+
+ <para>Note that this command cannot be used with <option>--runtime</option> or
+ to remotely edit units.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
@@ -1559,7 +1584,27 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
code otherwise.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <variablelist class='environment-variables'>
+ <varlistentry>
+ <term><varname>$SYSTEMD_EDITOR</varname></term>
+
+ <listitem><para>Editor to use when editing units; overrides
+ <varname>$EDITOR</varname> and <varname>$VISUAL</varname>. If neither
+ <varname>$SYSTEMD_EDITOR</varname> nor <varname>$EDITOR</varname> nor
+ <varname>$VISUAL</varname> are present or if it is set to an empty
+ string or if their execution failed, systemctl will try to execute well
+ known editors in this order:
+ <citerefentry><refentrytitle>nano</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>vim</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>vi</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>See Also</title>
@@ -1572,7 +1617,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<citerefentry><refentrytitle>systemd.resource-management</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 073e807..0dd21a5 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -383,7 +383,11 @@ Service b at 0.service not loaded, b.socket cannot be started.
</example>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/timedatectl.xml b/man/timedatectl.xml
index f3edb8d..849cc06 100644
--- a/man/timedatectl.xml
+++ b/man/timedatectl.xml
@@ -197,7 +197,11 @@
code otherwise.</para>
</refsect1>
- <xi:include href="less-variables.xml" />
+ <refsect1>
+ <title>Environment</title>
+
+ <xi:include href="less-variables.xml" />
+ </refsect1>
<refsect1>
<title>Examples</title>
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 28eaa6a..3721610 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -72,6 +72,8 @@
#include "bus-message.h"
#include "bus-error.h"
#include "bus-errors.h"
+#include "copy.h"
+#include "mkdir.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
@@ -1965,28 +1967,18 @@ static int set_default(sd_bus *bus, char **args) {
r = 0;
} else {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "SetDefaultTarget");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "sb", unit, 1);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ "SetDefaultTarget",
+ &error,
+ &reply,
+ "sb", unit, true);
if (r < 0) {
log_error("Failed to set default target: %s", bus_error_message(&error, -r));
return r;
@@ -2153,7 +2145,6 @@ static int list_jobs(sd_bus *bus, char **args) {
static int cancel_job(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
char **name;
- int r = 0;
assert(args);
@@ -2161,43 +2152,31 @@ static int cancel_job(sd_bus *bus, char **args) {
return daemon_reload(bus, args);
STRV_FOREACH(name, args+1) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
uint32_t id;
- int q;
+ int r;
- q = safe_atou32(*name, &id);
- if (q < 0) {
- log_error("Failed to parse job id \"%s\": %s", *name, strerror(-q));
- return q;
+ r = safe_atou32(*name, &id);
+ if (r < 0) {
+ log_error("Failed to parse job id \"%s\": %s", *name, strerror(-r));
+ return r;
}
- q = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "CancelJob");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(1);
-
- q = sd_bus_message_append(m, "u", id);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
- if (q < 0) {
- log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
- if (r == 0)
- r = q;
+ "CancelJob",
+ &error,
+ NULL,
+ "u", id);
+ if (r < 0) {
+ log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, r));
+ return r;
}
}
- return r;
+ return 0;
}
static int need_daemon_reload(sd_bus *bus, const char *unit) {
@@ -2590,7 +2569,7 @@ static int start_unit_one(
sd_bus_error *error,
Set *s) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
const char *path;
int r;
@@ -2600,26 +2579,15 @@ static int start_unit_one(
assert(error);
log_debug("Calling manager for %s on %s, %s", method, name, mode);
-
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "ss", name, mode);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, error, &reply);
+ method,
+ error,
+ &reply,
+ "ss", name, mode);
if (r < 0) {
const char *verb;
@@ -2861,7 +2829,7 @@ static int reboot_with_logind(sd_bus *bus, enum action a) {
method,
&error,
NULL,
- "b", arg_ask_password);
+ "b", true);
if (r < 0)
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
@@ -3084,29 +3052,18 @@ static int kill_unit(sd_bus *bus, char **args) {
log_error("Failed to expand names: %s", strerror(-r));
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "KillUnit");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "ssi", *names, arg_kill_who, arg_signal);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi", *names, arg_kill_who, arg_signal);
if (q < 0) {
- log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
+ log_error("Failed to kill unit %s: %s",
+ *names, bus_error_message(&error, r));
if (r == 0)
r = q;
}
@@ -4606,10 +4563,6 @@ static int set_property(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
n = unit_name_mangle(args[1], MANGLE_NOGLOB);
if (!n)
return log_oom();
@@ -4651,7 +4604,7 @@ static int set_property(sd_bus *bus, char **args) {
static int snapshot(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *n = NULL, *id = NULL;
const char *path;
int r;
@@ -4663,25 +4616,15 @@ static int snapshot(sd_bus *bus, char **args) {
if (!n)
return log_oom();
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "CreateSnapshot");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "sb", n, false);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ "CreateSnapshot",
+ &error,
+ &reply,
+ "sb", n, false);
if (r < 0) {
log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
return r;
@@ -4714,7 +4657,7 @@ static int delete_snapshot(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
char **name;
- int r;
+ int r, q;
assert(args);
@@ -4723,30 +4666,18 @@ static int delete_snapshot(sd_bus *bus, char **args) {
log_error("Failed to expand names: %s", strerror(-r));
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int q;
-
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "RemoveSnapshot");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "s", *name);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "RemoveSnapshot",
+ &error,
+ NULL,
+ "s", *name);
if (q < 0) {
- log_error("Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q));
+ log_error("Failed to remove snapshot %s: %s",
+ *name, bus_error_message(&error, r));
if (r == 0)
r = q;
}
@@ -4757,7 +4688,6 @@ static int delete_snapshot(sd_bus *bus, char **args) {
static int daemon_reload(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
const char *method;
int r;
@@ -4781,21 +4711,16 @@ static int daemon_reload(sd_bus *bus, char **args) {
/* "daemon-reload" */ "Reload";
}
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
+ method,
+ &error,
+ NULL,
+ NULL);
- r = sd_bus_call(bus, m, 0, &error, NULL);
if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
/* There's always a fallback possible for
* legacy actions. */
@@ -4824,29 +4749,18 @@ static int reset_failed(sd_bus *bus, char **args) {
log_error("Failed to expand names: %s", strerror(-r));
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- q = sd_bus_message_new_method_call(
+ q = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ResetFailedUnit");
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_message_append(m, "s", *name);
- if (q < 0)
- return bus_log_create_error(q);
-
- q = sd_bus_call(bus, m, 0, &error, NULL);
+ "ResetFailedUnit",
+ &error,
+ NULL,
+ "s", *name);
if (q < 0) {
- log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
+ log_error("Failed to reset failed state of unit %s: %s",
+ *name, bus_error_message(&error, r));
if (r == 0)
r = q;
}
@@ -4977,10 +4891,6 @@ static int set_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append_strv(m, args + 1);
if (r < 0)
return bus_log_create_error(r);
@@ -5012,10 +4922,6 @@ static int import_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
if (strv_isempty(args + 1))
r = sd_bus_message_append_strv(m, environ);
else {
@@ -5327,10 +5233,6 @@ static int enable_unit(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append_strv(m, names);
if (r < 0)
return bus_log_create_error(r);
@@ -5446,15 +5348,23 @@ static int add_dependency(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ r = sd_bus_message_append_strv(m, names);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(m, names);
+ r = sd_bus_message_append(m, "s", target);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "ssbb", target, unit_dependency_to_string(dep), arg_runtime, arg_force);
+ r = sd_bus_message_append(m, "s", unit_dependency_to_string(dep));
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "b", arg_runtime);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "b", arg_force);
if (r < 0)
return bus_log_create_error(r);
@@ -5496,33 +5406,21 @@ static int preset_all(sd_bus *bus, char **args) {
r = 0;
} else {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- r = sd_bus_message_new_method_call(
+ r = sd_bus_call_method(
bus,
- &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "PresetAllUnitFiles");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
+ "PresetAllUnitFiles",
+ &error,
+ &reply,
"sbb",
unit_file_preset_mode_to_string(arg_preset_mode),
arg_runtime,
arg_force);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
return r;
@@ -5642,6 +5540,393 @@ static int is_system_running(sd_bus *bus, char **args) {
return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE;
}
+static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) {
+ char **p;
+
+ assert(lp);
+ assert(unit_name);
+ assert(unit_path);
+
+ STRV_FOREACH(p, lp->unit_path) {
+ char *path;
+
+ path = strjoin(*p, "/", unit_name, NULL);
+ if (!path)
+ return log_oom();
+
+ if (access(path, F_OK) == 0) {
+ *unit_path = path;
+ return 1;
+ }
+
+ free(path);
+ }
+
+ return 0;
+}
+
+static int unit_file_drop_in(const char *unit_name, const char *config_home, char **new_path) {
+ char *tmp_path;
+ int r;
+
+ assert(unit_name);
+ assert(new_path);
+
+ switch (arg_scope) {
+ case UNIT_FILE_SYSTEM:
+ tmp_path = strjoin(SYSTEM_CONFIG_UNIT_PATH, "/", unit_name, ".d/amendments.conf", NULL);
+ break;
+ case UNIT_FILE_GLOBAL:
+ tmp_path = strjoin(USER_CONFIG_UNIT_PATH, "/", unit_name, ".d/amendments.conf", NULL);
+ break;
+ case UNIT_FILE_USER:
+ assert(config_home);
+ tmp_path = strjoin(config_home, "/", unit_name, ".d/amendments.conf", NULL);
+ break;
+ default:
+ assert_not_reached("Invalid scope");
+ }
+ if (!tmp_path)
+ return log_oom();
+
+ r = mkdir_parents(tmp_path, 0755);
+ if (r < 0) {
+ log_error("Failed to create directories for %s: %s", tmp_path, strerror(-r));
+ free(tmp_path);
+ return r;
+ }
+
+ *new_path = tmp_path;
+
+ return 0;
+}
+
+static int unit_file_copy_if_needed(const char *unit_name, const char *fragment_path, char **new_path) {
+ char *tmp_path;
+ int r;
+
+ assert(fragment_path);
+ assert(unit_name);
+ assert(new_path);
+
+ /* If it's a unit for the --user scope there is no need to copy it, it's already in the right directory.
+ * Same if this is --system/--global scope and the file is in {SYSTEM,USER}_CONFIG_UNIT_PATH
+ */
+ if (arg_scope == UNIT_FILE_USER
+ || startswith(fragment_path, SYSTEM_CONFIG_UNIT_PATH)
+ || startswith(fragment_path, USER_CONFIG_UNIT_PATH)) {
+ *new_path = strdup(fragment_path);
+ if (!*new_path)
+ return log_oom();
+ return 0;
+ }
+
+ switch (arg_scope) {
+ case UNIT_FILE_SYSTEM:
+ tmp_path = strjoin(SYSTEM_CONFIG_UNIT_PATH, "/", unit_name, NULL);
+ break;
+ case UNIT_FILE_GLOBAL:
+ tmp_path = strjoin(USER_CONFIG_UNIT_PATH, "/", unit_name, NULL);
+ break;
+ default:
+ assert_not_reached("Invalid scope");
+ }
+ if (!tmp_path)
+ return log_oom();
+
+ if (access(tmp_path, F_OK) == 0) {
+ char response;
+
+ r = ask_char(&response, "yn", "%s already exists, are you sure to overwrite it with %s? [(y)es, (n)o] ", tmp_path, fragment_path);
+ if (r < 0) {
+ free(tmp_path);
+ return r;
+ }
+ if (response != 'y') {
+ log_warning("%s ignored", unit_name);
+ free(tmp_path);
+ return -1;
+ }
+ }
+
+ r = mkdir_parents(tmp_path, 0755);
+ if (r < 0) {
+ log_error("Failed to create directories for %s: %s", tmp_path, strerror(-r));
+ free(tmp_path);
+ return r;
+ }
+
+ r = copy_file(fragment_path, tmp_path, 0, 0644);
+ if (r < 0) {
+ log_error("Failed to copy %s to %s: %s", fragment_path, tmp_path, strerror(-r));
+ free(tmp_path);
+ return r;
+ }
+
+ *new_path = tmp_path;
+
+ return 0;
+}
+
+static int get_editors(char ***editors) {
+ char **tmp_editors = strv_new("nano", "vim", "vi", NULL);
+ char *editor;
+
+ /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL
+ * If neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present,
+ * we try to execute well known editors
+ */
+ editor = getenv("SYSTEMD_EDITOR");
+ if (!editor)
+ editor = getenv("EDITOR");
+ if (!editor)
+ editor = getenv("VISUAL");
+
+ if (editor) {
+ int r;
+
+ editor = strdup(editor);
+ if (!editor)
+ return log_oom();
+
+ r = strv_consume_prepend(&tmp_editors, editor);
+ if (r < 0)
+ return log_oom();
+ }
+
+ *editors = tmp_editors;
+
+ return 0;
+}
+
+static int run_editor(char **paths) {
+ pid_t pid;
+ siginfo_t status;
+ int r;
+
+ assert(paths);
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ return -errno;
+ }
+
+ if (pid == 0) {
+ _cleanup_strv_free_ char **editors = NULL;
+ char *editor;
+ char **p;
+
+ r = get_editors(&editors);
+ if (r < 0) {
+ _exit(EXIT_FAILURE);
+ }
+
+ STRV_FOREACH(p, editors) {
+ _cleanup_strv_free_ char **args = NULL;
+
+ editor = strdup(*p);
+ if (!editor) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
+
+ args = strv_copy(paths);
+ if (!args) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
+
+ r = strv_consume_prepend(&args, editor);
+ if (r < 0) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
+
+ execvp(editor, args);
+ /* We do not fail if the editor doesn't exist
+ * because we want to try each one of them before
+ * failing.
+ */
+ if (errno != ENOENT) {
+ log_error("Failed to execute %s: %m", editor);
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ log_error("Cannot edit unit(s): No editor available. Please set either SYSTEMD_EDITOR or EDITOR or VISUAL environment variable");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = wait_for_terminate(pid, &status);
+ if (r < 0) {
+ log_error("Failed to wait for child: %s", strerror(-r));
+ return r;
+ }
+
+ return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+}
+
+static int find_units_path(sd_bus *bus, char **names, char ***paths) {
+ _cleanup_free_ char *config_home = NULL;
+ _cleanup_free_ char *unit = NULL;
+ char **name;
+ int r;
+
+ assert(names);
+ assert(paths);
+
+ if (arg_scope == UNIT_FILE_USER) {
+ r = user_config_home(&config_home);
+ if (r < 0)
+ return log_oom();
+
+ if (r == 0) {
+ log_error("Cannot edit units for the user instance: home directory unknown");
+ return -1;
+ }
+ }
+
+ if (!bus || avoid_bus()) {
+ _cleanup_lookup_paths_free_ LookupPaths lp = {};
+
+ /* If there is no bus, we try to find the units by testing each available directory
+ * according to the scope.
+ */
+ 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) {
+ log_error("Cannot get lookup paths: %s", strerror(-r));
+ return r;
+ }
+
+ STRV_FOREACH(name, names) {
+ _cleanup_free_ char *path = NULL;
+ char *new_path;
+
+ r = unit_file_find_path(&lp, *name, &path);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_warning("%s ignored: not found", *name);
+ continue;
+ }
+
+ if (arg_full)
+ r = unit_file_copy_if_needed(*name, path, &new_path);
+ else
+ r = unit_file_drop_in(*name, config_home, &new_path);
+
+ if (r < 0)
+ continue;
+
+ r = strv_push(paths, new_path);
+ if (r < 0)
+ return log_oom();
+ }
+ } else {
+ STRV_FOREACH(name, names) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *fragment_path = NULL;
+ char *new_path;
+
+ unit = unit_dbus_path_from_name(*name);
+ if (!unit)
+ return log_oom();
+
+ if (need_daemon_reload(bus, *name) > 0) {
+ log_warning("%s ignored: unit file changed on disk. Run 'systemctl%s daemon-reload'.",
+ *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+ continue;
+ }
+
+ 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;
+ }
+
+ if (isempty(fragment_path)) {
+ log_warning("%s ignored: not found", *name);
+ continue;
+ }
+
+ if (arg_full)
+ r = unit_file_copy_if_needed(*name, fragment_path, &new_path);
+ else
+ r = unit_file_drop_in(*name, config_home, &new_path);
+ if (r < 0)
+ continue;
+
+ r = strv_push(paths, new_path);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+
+ return 0;
+}
+
+static int edit(sd_bus *bus, char **args) {
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_strv_free_ char **paths = NULL;
+ int r;
+
+ assert(args);
+
+ if (!on_tty())
+ return 0;
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL) {
+ log_error("Cannot remotely edit units");
+ return -EINVAL;
+ }
+
+ if (arg_runtime) {
+ log_error("Cannot edit runtime units");
+ return -EINVAL;
+ }
+
+ r = expand_names(bus, args + 1, NULL, &names);
+ if (r < 0) {
+ log_error("Failed to expand names: %s", strerror(-r));
+ return r;
+ }
+
+ if (!names) {
+ log_error("No unit name found by expanding names");
+ return -ENOENT;
+ }
+
+ r = find_units_path(bus, names, &paths);
+ if (r < 0)
+ return r;
+
+ if (strv_isempty(paths)) {
+ log_error("Cannot find any units to edit");
+ return -ENOENT;
+ }
+
+ r = run_editor(paths);
+ if (r < 0)
+ return r;
+
+ if (!arg_no_reload)
+ r = daemon_reload(bus, args);
+
+ return r;
+}
+
static void systemctl_help(void) {
pager_open_if_enabled();
@@ -5739,7 +6024,9 @@ static void systemctl_help(void) {
" add-requires TARGET NAME... Add 'Requires' dependency for the target\n"
" on specified one or more units\n"
" get-default Get the name of the default target\n"
- " set-default NAME Set the default target\n\n"
+ " set-default NAME Set the default target\n"
+ " edit NAME... Edit one or more unit files\n"
+ "\n"
"Machine Commands:\n"
" list-machines [PATTERN...] List local containers and host\n\n"
"Job Commands:\n"
@@ -6750,6 +7037,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
{ "is-system-running", EQUAL, 1, is_system_running },
{ "add-wants", MORE, 3, add_dependency, NOBUS },
{ "add-requires", MORE, 3, add_dependency, NOBUS },
+ { "edit", MORE, 2, edit, NOBUS },
{}
}, *verb = verbs;
--
2.1.2
More information about the systemd-devel
mailing list