1ff636
From 65d36d59c936650e141fcbf38b287627fd0ba21a Mon Sep 17 00:00:00 2001
1ff636
From: Jan Synacek <jsynacek@redhat.com>
1ff636
Date: Fri, 15 May 2015 09:54:10 +0200
1ff636
Subject: [PATCH] systemctl: introduce --now for enable, disable and mask
1ff636
1ff636
https://bugs.freedesktop.org/show_bug.cgi?id=42940
1ff636
1ff636
Conflicts:
1ff636
	src/libsystemd/sd-bus/bus-util.c
1ff636
	src/libsystemd/sd-bus/bus-util.h
1ff636
	src/systemctl/systemctl.c
1ff636
1ff636
Cherry-picked from: 57ab2eabb8f92fad5239c7d4492e9c6e23ee0678
1ff636
Resolves: #1233081
1ff636
---
1ff636
 Makefile.am                      |   1 +
23b3cf
 man/systemctl.xml                |  33 ++++++---
23b3cf
 src/libsystemd/sd-bus/bus-util.c |   6 +-
1ff636
 src/libsystemd/sd-bus/bus-util.h |   3 +-
1ff636
 src/machine/machinectl.c         |   2 +-
23b3cf
 src/shared/install.c             | 112 +++++++++++++++----------------
1ff636
 src/shared/install.h             |   1 +
23b3cf
 src/systemctl/systemctl.c        |  28 ++++++--
1ff636
 8 files changed, 114 insertions(+), 72 deletions(-)
1ff636
1ff636
diff --git a/Makefile.am b/Makefile.am
181b3f
index 604eaf2f1..d3fb398fe 100644
1ff636
--- a/Makefile.am
1ff636
+++ b/Makefile.am
1ff636
@@ -5253,6 +5253,7 @@ machinectl_LDADD = \
1ff636
 	libsystemd-internal.la \
1ff636
 	libsystemd-logs.la \
1ff636
 	libsystemd-journal-internal.la \
1ff636
+	libsystemd-units.la \
1ff636
 	libsystemd-shared.la
1ff636
 
1ff636
 rootbin_PROGRAMS += \
1ff636
diff --git a/man/systemctl.xml b/man/systemctl.xml
181b3f
index 3c4c9cb92..44ec0d7bc 100644
1ff636
--- a/man/systemctl.xml
1ff636
+++ b/man/systemctl.xml
181b3f
@@ -455,6 +455,18 @@
181b3f
         </listitem>
1ff636
       </varlistentry>
1ff636
 
181b3f
+      <varlistentry>
1ff636
+        <term><option>--now</option></term>
1ff636
+
1ff636
+        <listitem>
1ff636
+          <para>When used with <command>enable</command>, the units
1ff636
+          will also be started. When used with <command>disable</command> or
1ff636
+          <command>mask</command>, the units will also be stopped. The start
1ff636
+          or stop operation is only carried out when the respective enable or
1ff636
+          disable operation has been successful.</para>
1ff636
+        </listitem>
1ff636
+      </varlistentry>
1ff636
+
181b3f
       <varlistentry>
1ff636
         <term><option>--root=</option></term>
1ff636
 
1ff636
@@ -909,11 +921,12 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
1ff636
             the changes are taken into account immediately. Note that
1ff636
             this does <emphasis>not</emphasis> have the effect of also
1ff636
             starting any of the units being enabled. If this
1ff636
-            is desired, a separate <command>start</command> command must
1ff636
-            be invoked for the unit. Also note that in case of instance
1ff636
-            enablement, symlinks named the same as instances are created in
1ff636
-            the install location, however they all point to the same
1ff636
-            template unit file.</para>
1ff636
+            is desired, either <option>--now</option> should be used
1ff636
+            together with this command, or an additional <command>start</command>
1ff636
+            command must be invoked for the unit. Also note that in case of
1ff636
+            instance enablement, symlinks named the same as instances
1ff636
+            are created in the install location, however they all point to the
1ff636
+            same template unit file.</para>
1ff636
 
1ff636
             <para>This command will print the actions executed. This
1ff636
             output may be suppressed by passing <option>--quiet</option>.
1ff636
@@ -968,9 +981,10 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
1ff636
             <command>enable</command>. This call implicitly reloads the
1ff636
             systemd daemon configuration after completing the disabling
1ff636
             of the units. Note that this command does not implicitly
1ff636
-            stop the units that are being disabled. If this is desired,
1ff636
-            an additional <command>stop</command> command should be
1ff636
-            executed afterwards.</para>
1ff636
+            stop the units that are being disabled. If this is desired, either
1ff636
+            <option>--now</option> should be used together with this command, or
1ff636
+            an additional <command>stop</command> command should be executed
1ff636
+            afterwards.</para>
1ff636
 
1ff636
             <para>This command will print the actions executed. This
1ff636
             output may be suppressed by passing <option>--quiet</option>.
1ff636
@@ -1116,7 +1130,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
1ff636
             activation of the unit, including enablement and manual
1ff636
             activation. Use this option with care. This honors the
1ff636
             <option>--runtime</option> option to only mask temporarily
1ff636
-            until the next reboot of the system.</para>
1ff636
+            until the next reboot of the system. The <option>--now</option>
1ff636
+            option can be used to ensure that the units are also stopped.</para>
1ff636
           </listitem>
1ff636
         </varlistentry>
1ff636
 
1ff636
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
181b3f
index 2e6d88962..fff00d9f9 100644
1ff636
--- a/src/libsystemd/sd-bus/bus-util.c
1ff636
+++ b/src/libsystemd/sd-bus/bus-util.c
1ff636
@@ -1849,7 +1849,7 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1ff636
         return set_put_strdup(d->jobs, path);
1ff636
 }
1ff636
 
1ff636
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
1ff636
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1ff636
         const char *type, *path, *source;
1ff636
         int r;
1ff636
 
1ff636
@@ -1864,6 +1864,10 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
1ff636
                         else
1ff636
                                 log_info("Removed symlink %s.", path);
1ff636
                 }
1ff636
+
1ff636
+                r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source);
1ff636
+                if (r < 0)
1ff636
+                        return r;
1ff636
         }
1ff636
         if (r < 0)
1ff636
                 return bus_log_parse_error(r);
1ff636
diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h
181b3f
index e8a97cef9..21db98228 100644
1ff636
--- a/src/libsystemd/sd-bus/bus-util.h
1ff636
+++ b/src/libsystemd/sd-bus/bus-util.h
1ff636
@@ -24,6 +24,7 @@
1ff636
 #include "sd-event.h"
1ff636
 #include "sd-bus.h"
1ff636
 #include "hashmap.h"
1ff636
+#include "install.h"
1ff636
 #include "time-util.h"
1ff636
 #include "util.h"
1ff636
 
1ff636
@@ -211,4 +212,4 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet);
1ff636
 
1ff636
 DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
1ff636
 
1ff636
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet);
1ff636
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
1ff636
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
181b3f
index 9f8c68b18..f1910709d 100644
1ff636
--- a/src/machine/machinectl.c
1ff636
+++ b/src/machine/machinectl.c
1ff636
@@ -1709,7 +1709,7 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
1ff636
                         return bus_log_parse_error(r);
1ff636
         }
1ff636
 
1ff636
-        r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1ff636
+        r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1ff636
         if (r < 0)
1ff636
                 return r;
1ff636
 
1ff636
diff --git a/src/shared/install.c b/src/shared/install.c
181b3f
index efd489ec0..b62065be5 100644
1ff636
--- a/src/shared/install.c
1ff636
+++ b/src/shared/install.c
1ff636
@@ -113,51 +113,6 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
1ff636
         return 0;
1ff636
 }
1ff636
 
1ff636
-static int add_file_change(
1ff636
-                UnitFileChange **changes,
1ff636
-                unsigned *n_changes,
1ff636
-                UnitFileChangeType type,
1ff636
-                const char *path,
1ff636
-                const char *source) {
1ff636
-
1ff636
-        UnitFileChange *c;
1ff636
-        unsigned i;
1ff636
-
1ff636
-        assert(path);
1ff636
-        assert(!changes == !n_changes);
1ff636
-
1ff636
-        if (!changes)
1ff636
-                return 0;
1ff636
-
1ff636
-        c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
1ff636
-        if (!c)
1ff636
-                return -ENOMEM;
1ff636
-
1ff636
-        *changes = c;
1ff636
-        i = *n_changes;
1ff636
-
1ff636
-        c[i].type = type;
1ff636
-        c[i].path = strdup(path);
1ff636
-        if (!c[i].path)
1ff636
-                return -ENOMEM;
1ff636
-
1ff636
-        path_kill_slashes(c[i].path);
1ff636
-
1ff636
-        if (source) {
1ff636
-                c[i].source = strdup(source);
1ff636
-                if (!c[i].source) {
1ff636
-                        free(c[i].path);
1ff636
-                        return -ENOMEM;
1ff636
-                }
1ff636
-
1ff636
-                path_kill_slashes(c[i].path);
1ff636
-        } else
1ff636
-                c[i].source = NULL;
1ff636
-
1ff636
-        *n_changes = i+1;
1ff636
-        return 0;
1ff636
-}
1ff636
-
1ff636
 static int mark_symlink_for_removal(
1ff636
                 Set **remove_symlinks_to,
1ff636
                 const char *p) {
1ff636
@@ -310,7 +265,7 @@ static int remove_marked_symlinks_fd(
1ff636
 
1ff636
                         path_kill_slashes(p);
1ff636
                         rmdir_parents(p, config_path);
1ff636
-                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
1ff636
+                        unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
1ff636
 
1ff636
                         if (!set_get(remove_symlinks_to, p)) {
1ff636
 
1ff636
@@ -597,7 +552,7 @@ int unit_file_mask(
1ff636
                 }
1ff636
 
1ff636
                 if (symlink("/dev/null", path) >= 0) {
1ff636
-                        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
1ff636
+                        unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
1ff636
                         continue;
1ff636
                 }
1ff636
 
1ff636
@@ -608,8 +563,8 @@ int unit_file_mask(
1ff636
 
1ff636
                         if (force) {
1ff636
                                 if (symlink_atomic("/dev/null", path) >= 0) {
1ff636
-                                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
-                                        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
1ff636
+                                        unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
+                                        unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
1ff636
                                         continue;
1ff636
                                 }
1ff636
                         }
1ff636
@@ -665,7 +620,7 @@ int unit_file_unmask(
1ff636
                                 q = -errno;
1ff636
                         else {
1ff636
                                 q = mark_symlink_for_removal(&remove_symlinks_to, path);
1ff636
-                                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
+                                unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
                         }
1ff636
                 }
1ff636
 
1ff636
@@ -747,7 +702,7 @@ int unit_file_link(
1ff636
                         return -ENOMEM;
1ff636
 
1ff636
                 if (symlink(*i, path) >= 0) {
1ff636
-                        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
1ff636
+                        unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
1ff636
                         continue;
1ff636
                 }
1ff636
 
1ff636
@@ -766,8 +721,8 @@ int unit_file_link(
1ff636
 
1ff636
                         if (force) {
1ff636
                                 if (symlink_atomic(*i, path) >= 0) {
1ff636
-                                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
-                                        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
1ff636
+                                        unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1ff636
+                                        unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
1ff636
                                         continue;
1ff636
                                 }
1ff636
                         }
1ff636
@@ -794,6 +749,51 @@ void unit_file_list_free(Hashmap *h) {
1ff636
         hashmap_free(h);
1ff636
 }
1ff636
 
1ff636
+int unit_file_changes_add(
1ff636
+                UnitFileChange **changes,
1ff636
+                unsigned *n_changes,
1ff636
+                UnitFileChangeType type,
1ff636
+                const char *path,
1ff636
+                const char *source) {
1ff636
+
1ff636
+        UnitFileChange *c;
1ff636
+        unsigned i;
1ff636
+
1ff636
+        assert(path);
1ff636
+        assert(!changes == !n_changes);
1ff636
+
1ff636
+        if (!changes)
1ff636
+                return 0;
1ff636
+
1ff636
+        c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
1ff636
+        if (!c)
1ff636
+                return -ENOMEM;
1ff636
+
1ff636
+        *changes = c;
1ff636
+        i = *n_changes;
1ff636
+
1ff636
+        c[i].type = type;
1ff636
+        c[i].path = strdup(path);
1ff636
+        if (!c[i].path)
1ff636
+                return -ENOMEM;
1ff636
+
1ff636
+        path_kill_slashes(c[i].path);
1ff636
+
1ff636
+        if (source) {
1ff636
+                c[i].source = strdup(source);
1ff636
+                if (!c[i].source) {
1ff636
+                        free(c[i].path);
1ff636
+                        return -ENOMEM;
1ff636
+                }
1ff636
+
1ff636
+                path_kill_slashes(c[i].path);
1ff636
+        } else
1ff636
+                c[i].source = NULL;
1ff636
+
1ff636
+        *n_changes = i+1;
1ff636
+        return 0;
1ff636
+}
1ff636
+
1ff636
 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
1ff636
         unsigned i;
1ff636
 
1ff636
@@ -1199,7 +1199,7 @@ static int create_symlink(
1ff636
         mkdir_parents_label(new_path, 0755);
1ff636
 
1ff636
         if (symlink(old_path, new_path) >= 0) {
1ff636
-                add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1ff636
+                unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1ff636
                 return 0;
1ff636
         }
1ff636
 
1ff636
@@ -1220,8 +1220,8 @@ static int create_symlink(
1ff636
         if (r < 0)
1ff636
                 return r;
1ff636
 
1ff636
-        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1ff636
-        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1ff636
+        unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1ff636
+        unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1ff636
 
1ff636
         return 0;
1ff636
 }
1ff636
diff --git a/src/shared/install.h b/src/shared/install.h
181b3f
index 3ca39397e..d729e6ed1 100644
1ff636
--- a/src/shared/install.h
1ff636
+++ b/src/shared/install.h
1ff636
@@ -112,6 +112,7 @@ UnitFileState unit_file_get_state(
1ff636
 int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
1ff636
 
1ff636
 void unit_file_list_free(Hashmap *h);
1ff636
+int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
1ff636
 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
1ff636
 
1ff636
 int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
1ff636
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
181b3f
index 089c25f83..9898694d7 100644
1ff636
--- a/src/systemctl/systemctl.c
1ff636
+++ b/src/systemctl/systemctl.c
1ff636
@@ -138,6 +138,7 @@ static char *arg_host = NULL;
1ff636
 static unsigned arg_lines = 10;
1ff636
 static OutputMode arg_output = OUTPUT_SHORT;
1ff636
 static bool arg_plain = false;
1ff636
+static bool arg_now = false;
1ff636
 
1ff636
 static bool original_stdout_is_tty;
1ff636
 
1ff636
@@ -1979,7 +1980,7 @@ static int set_default(sd_bus *bus, char **args) {
1ff636
                         return r;
1ff636
                 }
1ff636
 
1ff636
-                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1ff636
+                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1ff636
                 if (r < 0)
1ff636
                         return r;
1ff636
 
1ff636
@@ -5415,7 +5416,7 @@ static int enable_unit(sd_bus *bus, char **args) {
1ff636
                                 return bus_log_parse_error(r);
1ff636
                 }
1ff636
 
1ff636
-                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1ff636
+                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
1ff636
                 if (r < 0)
1ff636
                         return r;
1ff636
 
1ff636
@@ -5437,6 +5438,18 @@ static int enable_unit(sd_bus *bus, char **args) {
1ff636
                             "3) A unit may be started when needed via activation (socket, path, timer,\n"
1ff636
                             "   D-Bus, udev, scripted systemctl call, ...).\n");
1ff636
 
1ff636
+        if (arg_now && n_changes > 0 && STR_IN_SET(args[0], "enable", "disable", "mask")) {
1ff636
+                char *new_args[n_changes + 2];
1ff636
+                unsigned i;
1ff636
+
1ff636
+                new_args[0] = streq(args[0], "enable") ? (char *)"start" : (char *)"stop";
1ff636
+                for (i = 0; i < n_changes; i++)
1ff636
+                        new_args[i + 1] = basename(changes[i].path);
1ff636
+                new_args[i + 1] = NULL;
1ff636
+
1ff636
+                r = start_unit(bus, new_args);
1ff636
+        }
1ff636
+
1ff636
 finish:
1ff636
         unit_file_changes_free(changes, n_changes);
1ff636
 
1ff636
@@ -5516,7 +5529,7 @@ static int add_dependency(sd_bus *bus, char **args) {
1ff636
                         return r;
1ff636
                 }
1ff636
 
1ff636
-                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1ff636
+                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1ff636
                 if (r < 0)
1ff636
                         return r;
1ff636
 
1ff636
@@ -5582,7 +5595,7 @@ static int preset_all(sd_bus *bus, char **args) {
1ff636
                         return r;
1ff636
                 }
1ff636
 
1ff636
-                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1ff636
+                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
1ff636
                 if (r < 0)
1ff636
                         return r;
1ff636
 
1ff636
@@ -6058,6 +6071,7 @@ static void systemctl_help(void) {
1ff636
                "                      When shutting down or sleeping, ignore inhibitors\n"
1ff636
                "     --kill-who=WHO   Who to send signal to\n"
1ff636
                "  -s --signal=SIGNAL  Which signal to send\n"
1ff636
+               "     --now            Start or stop unit in addition to enabling or disabling it\n"
1ff636
                "  -q --quiet          Suppress output\n"
1ff636
                "     --no-block       Do not wait until operation finished\n"
1ff636
                "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
1ff636
@@ -6255,6 +6269,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
1ff636
                 ARG_STATE,
1ff636
                 ARG_JOB_MODE,
1ff636
                 ARG_PRESET_MODE,
1ff636
+                ARG_NOW,
1ff636
         };
1ff636
 
1ff636
         static const struct option options[] = {
1ff636
@@ -6297,6 +6312,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
1ff636
                 { "state",               required_argument, NULL, ARG_STATE               },
1ff636
                 { "recursive",           no_argument,       NULL, 'r'                     },
1ff636
                 { "preset-mode",         required_argument, NULL, ARG_PRESET_MODE         },
1ff636
+                { "now",                 no_argument,       NULL, ARG_NOW                 },
1ff636
                 {}
1ff636
         };
1ff636
 
1ff636
@@ -6573,6 +6589,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
1ff636
 
1ff636
                         break;
1ff636
 
1ff636
+                case ARG_NOW:
1ff636
+                        arg_now = true;
1ff636
+                        break;
1ff636
+
1ff636
                 case '?':
1ff636
                         return -EINVAL;
1ff636