[systemd-devel] [PATCH v2] systemctl: add edit verb

Ronny Chevalier chevalier.ronny at gmail.com
Sat Oct 18 09:17:21 PDT 2014


Ignore this one, I rebased, then saved a modification in vim, so the
rebase is reverted in the patch.

Sorry for the noise.

2014-10-18 18:12 GMT+02:00 Ronny Chevalier <chevalier.ronny at gmail.com>:
> 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