[systemd-commits] 23 commits - Makefile.am TODO man/systemctl.xml man/systemd.unit.xml src/core src/shared src/systemctl src/test system-preset/90-systemd.preset system-preset/Makefile units/getty at .service.m4 units/systemd-networkd.service.in units/systemd-timesyncd.service.in

Lennart Poettering lennart at kemper.freedesktop.org
Mon Jun 16 18:11:26 PDT 2014


 Makefile.am                           |    9 
 TODO                                  |    2 
 man/systemctl.xml                     |   41 ++
 man/systemd.unit.xml                  |   13 
 src/core/dbus-manager.c               |  114 ++++++
 src/core/load-fragment-gperf.gperf.m4 |    1 
 src/core/main.c                       |    5 
 src/shared/conf-files.c               |   16 
 src/shared/install.c                  |  571 +++++++++++++++++++++-------------
 src/shared/install.h                  |   18 -
 src/shared/log.c                      |    3 
 src/shared/unit-name.c                |  102 +++---
 src/shared/util.c                     |   16 
 src/shared/util.h                     |    1 
 src/systemctl/systemctl.c             |  101 +++++-
 src/test/test-install.c               |    2 
 system-preset/90-systemd.preset       |   31 +
 system-preset/Makefile                |    1 
 units/getty at .service.m4               |    1 
 units/systemd-networkd.service.in     |    6 
 units/systemd-timesyncd.service.in    |    5 
 21 files changed, 750 insertions(+), 309 deletions(-)

New commits:
commit a1259e0a1481b3621ebcc285b76a2ea43ca223c8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 03:09:27 2014 +0200

    build-sys: touch /usr on "make install"
    
    this way, the "make install" will be detected as offline update on next
    boot.

diff --git a/Makefile.am b/Makefile.am
index 66eda99..0213c38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -297,10 +297,14 @@ define install-relative-aliases
 	done
 endef
 
+install-touch-usr-hook:
+	touch -c $(DESTDIR)/$(prefix)
+
 INSTALL_EXEC_HOOKS += \
 	install-target-wants-hook \
 	install-directories-hook \
-	install-aliases-hook
+	install-aliases-hook \
+	install-touch-usr-hook
 
 # ------------------------------------------------------------------------------
 AM_V_M4 = $(AM_V_M4_$(V))

commit 689d781b87164bc89554e2fecdf6fbb5725c0804
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 02:18:33 2014 +0200

    units: minor cleanups

diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in
index 8b0ea14..f33c65e 100644
--- a/units/systemd-networkd.service.in
+++ b/units/systemd-networkd.service.in
@@ -8,11 +8,12 @@
 [Unit]
 Description=Network Service
 Documentation=man:systemd-networkd.service(8)
+ConditionCapability=CAP_NET_ADMIN
 DefaultDependencies=no
 After=dbus.service network-pre.target systemd-sysusers.service
-Before=network.target
+Before=network.target multi-user.target shutdown.target
+Conflicts=shutdown.target
 Wants=network.target
-ConditionCapability=CAP_NET_ADMIN
 
 [Service]
 Type=notify
diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in
index a20621d..17b60d0 100644
--- a/units/systemd-timesyncd.service.in
+++ b/units/systemd-timesyncd.service.in
@@ -9,13 +9,12 @@
 Description=Network Time Synchronization
 Documentation=man:systemd-timesyncd.service(8)
 ConditionCapability=CAP_SYS_TIME
-DefaultDependencies=off
+DefaultDependencies=no
 RequiresMountsFor=/var/lib/systemd/clock
 After=systemd-remount-fs.service systemd-tmpfiles-setup.service systemd-sysusers.service
-Before=sysinit.target shutdown.target
+Before=time-sync.target sysinit.target shutdown.target
 Conflicts=shutdown.target
 Wants=time-sync.target
-Before=time-sync.target
 
 [Service]
 Type=notify

commit ac78d81a35fae1c10464992dac25f1527a05cbc9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 02:15:03 2014 +0200

    install: improve paths we show the user when enabling/disabling

diff --git a/src/shared/install.c b/src/shared/install.c
index afb2e53..c7cb522 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -160,12 +160,16 @@ static int add_file_change(
         if (!c[i].path)
                 return -ENOMEM;
 
+        path_kill_slashes(c[i].path);
+
         if (source) {
                 c[i].source = strdup(source);
                 if (!c[i].source) {
                         free(c[i].path);
                         return -ENOMEM;
                 }
+
+                path_kill_slashes(c[i].path);
         } else
                 c[i].source = NULL;
 

commit 324d7a53b9c53b33520b97f2562219ac4143f581
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 02:13:08 2014 +0200

    networkd: don't pull in systemd-networkd-wait-online service from systemd-networkd when enabling
    
    networkd-wait-online should never exist in the default transaction,
    unless explicitly enable or pulled in via things like NFS. However, just
    enabling networkd shouldn't enable networkd-wait-online, since it's
    common to use the former without the latter.

diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in
index 57c3fbf..8b0ea14 100644
--- a/units/systemd-networkd.service.in
+++ b/units/systemd-networkd.service.in
@@ -25,5 +25,4 @@ ProtectHome=yes
 WatchdogSec=1min
 
 [Install]
-Also=systemd-networkd-wait-online.service
 WantedBy=multi-user.target

commit cebed5005b5ede17fc52ab50c054fca73bc938b8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:56:44 2014 +0200

    conf-files: fix when for --root= logic
    
    This is based on parts of similar patches from Michael Marineau and
    Lukas Nykrin, but simply uses strappenda3().

diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index 5201782..59bc8ce 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -37,10 +37,20 @@
 #include "hashmap.h"
 #include "conf-files.h"
 
-static int files_add(Hashmap *h, const char *dirpath, const char *suffix) {
+static int files_add(Hashmap *h, const char *dirpath, const char *suffix, const char *root) {
         _cleanup_closedir_ DIR *dir = NULL;
 
-        dir = opendir(dirpath);
+        assert(dirpath);
+        assert(suffix);
+
+        if (isempty(root))
+                dir = opendir(dirpath);
+        else {
+                const char *p;
+
+                p = strappenda3(root, "/", dirpath);
+                dir = opendir(p);
+        }
         if (!dir) {
                 if (errno == ENOENT)
                         return 0;
@@ -108,7 +118,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
                 return -ENOMEM;
 
         STRV_FOREACH(p, dirs) {
-                r = files_add(fh, *p, suffix);
+                r = files_add(fh, *p, suffix, root);
                 if (r == -ENOMEM) {
                         hashmap_free_free(fh);
                         return r;

commit 749ebb2da4933de68bfaa4d6f6ffd9e4692ee547
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:38:07 2014 +0200

    systemctl: output human readable strings in "systemctl enable" and "systemctl disable"

diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 56f5084..fc6329c 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -1928,9 +1928,9 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha
 
         for (i = 0; i < n_changes; i++) {
                 if (changes[i].type == UNIT_FILE_SYMLINK)
-                        log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+                        log_info("Created symlink from %s to %s.", changes[i].source, changes[i].path);
                 else
-                        log_info("rm '%s'", changes[i].path);
+                        log_info("Removed symlink %s.", changes[i].path);
         }
 }
 
@@ -1945,9 +1945,9 @@ static int deserialize_and_dump_unit_file_changes(sd_bus_message *m) {
         while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
                 if (!arg_quiet) {
                         if (streq(type, "symlink"))
-                                log_info("ln -s '%s' '%s'", source, path);
+                                log_info("Created symlink from %s to %s.", source, path);
                         else
-                                log_info("rm '%s'", path);
+                                log_info("Removed symlink %s.", path);
                 }
         }
         if (r < 0)

commit 278fa5758c8e30f03c8c50f15873d55edfc4cbaf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:37:54 2014 +0200

    install: simplify symlink --root= logic

diff --git a/src/shared/install.c b/src/shared/install.c
index 39f5dd2..afb2e53 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -47,9 +47,8 @@ typedef struct {
 
 #define _cleanup_install_context_done_ _cleanup_(install_context_done)
 
-static int in_search_path(const char *path, char **search, const char *root_dir) {
+static int in_search_path(const char *path, char **search) {
         _cleanup_free_ char *parent = NULL;
-        char **i;
         int r;
 
         assert(path);
@@ -58,24 +57,7 @@ static int in_search_path(const char *path, char **search, const char *root_dir)
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(i, search) {
-                _cleanup_free_ char *buf = NULL;
-                const char *p;
-
-                if (root_dir) {
-                        buf = strjoin(root_dir, "/", *i, NULL);
-                        if (!buf)
-                                return -ENOMEM;
-
-                        p = buf;
-                } else
-                        p = *i;
-
-                if (path_equal(parent, p))
-                        return 1;
-        }
-
-        return 0;
+        return strv_contains(search, parent);
 }
 
 static int lookup_paths_init_from_scope(LookupPaths *paths,
@@ -777,7 +759,7 @@ int unit_file_link(
                         continue;
                 }
 
-                q = in_search_path(*i, paths.unit_path, root_dir);
+                q = in_search_path(*i, paths.unit_path);
                 if (q < 0)
                         return q;
 
@@ -1057,6 +1039,7 @@ static int unit_file_load(
                 InstallContext *c,
                 InstallInfo *info,
                 const char *path,
+                const char *root_dir,
                 bool allow_symlink) {
 
         const ConfigTableItem items[] = {
@@ -1069,14 +1052,16 @@ static int unit_file_load(
                 {}
         };
 
-        int fd;
         _cleanup_fclose_ FILE *f = NULL;
-        int r;
+        int fd, r;
 
         assert(c);
         assert(info);
         assert(path);
 
+        if (!isempty(root_dir))
+                path = strappenda3(root_dir, "/", path);
+
         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
         if (fd < 0)
                 return -errno;
@@ -1111,30 +1096,19 @@ static int unit_file_search(
         assert(info);
         assert(paths);
 
-        if (info->path) {
-                const char *path;
-
-                if (isempty(root_dir))
-                        path = info->path;
-                else
-                        path = strappenda(root_dir, info->path);
-
-                return unit_file_load(c, info, path, allow_symlink);
-        }
+        if (info->path)
+                return unit_file_load(c, info, info->path, root_dir, allow_symlink);
 
         assert(info->name);
 
         STRV_FOREACH(p, paths->unit_path) {
                 _cleanup_free_ char *path = NULL;
 
-                if (isempty(root_dir))
-                        path = strjoin(*p, "/", info->name, NULL);
-                else
-                        path = strjoin(root_dir, "/", *p, "/", info->name, NULL);
+                path = strjoin(*p, "/", info->name, NULL);
                 if (!path)
                         return -ENOMEM;
 
-                r = unit_file_load(c, info, path, allow_symlink);
+                r = unit_file_load(c, info, path, root_dir, allow_symlink);
                 if (r >= 0) {
                         info->path = path;
                         path = NULL;
@@ -1159,14 +1133,11 @@ static int unit_file_search(
                 STRV_FOREACH(p, paths->unit_path) {
                         _cleanup_free_ char *path = NULL;
 
-                        if (isempty(root_dir))
-                                path = strjoin(*p, "/", template, NULL);
-                        else
-                                path = strjoin(root_dir, "/", *p, "/", template, NULL);
+                        path = strjoin(*p, "/", template, NULL);
                         if (!path)
                                 return -ENOMEM;
 
-                        r = unit_file_load(c, info, path, allow_symlink);
+                        r = unit_file_load(c, info, path, root_dir, allow_symlink);
                         if (r >= 0) {
                                 info->path = path;
                                 path = NULL;
@@ -1359,7 +1330,7 @@ static int install_info_symlink_link(
         assert(config_path);
         assert(i->path);
 
-        r = in_search_path(i->path, paths->unit_path, root_dir);
+        r = in_search_path(i->path, paths->unit_path);
         if (r != 0)
                 return r;
 

commit 0a327d753f992ffdcff5f38c861c90bfa1f5a4af
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:22:55 2014 +0200

    install: "systemctl enable" should be a nop for template units lacking a DefaultInstance= setting

diff --git a/src/shared/install.c b/src/shared/install.c
index 26dbbb6..39f5dd2 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1302,7 +1302,14 @@ static int install_info_symlink_wants(
         assert(i);
         assert(config_path);
 
-        if (unit_name_is_template(i->name) && i->default_instance) {
+        if (unit_name_is_template(i->name)) {
+
+                /* Don't install any symlink if there's no default
+                 * instance configured */
+
+                if (!i->default_instance)
+                        return 0;
+
                 buf = unit_name_replace_instance(i->name, i->default_instance);
                 if (!buf)
                         return -ENOMEM;

commit 1713703c966d85de2591498b45b6c035d04350aa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:14:44 2014 +0200

    preset: ship a minimal preset file upstream
    
    This is useful so that distros have something to base their own policy
    of. It also useful to make sure that minimal installs always get useful
    configuration in place.

diff --git a/Makefile.am b/Makefile.am
index a9e07b4..66eda99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -509,6 +509,9 @@ dist_userunit_DATA = \
 nodist_userunit_DATA = \
 	units/user/systemd-exit.service
 
+dist_systempreset_DATA = \
+	system-preset/90-systemd.preset
+
 EXTRA_DIST += \
 	units/getty at .service.m4 \
 	units/serial-getty at .service.m4 \
diff --git a/system-preset/90-systemd.preset b/system-preset/90-systemd.preset
new file mode 100644
index 0000000..e4a9e17
--- /dev/null
+++ b/system-preset/90-systemd.preset
@@ -0,0 +1,31 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+# These ones should be enabled by default, even if distributions
+# generally follow a default-off policy.
+
+enable remote-fs.target
+enable getty at .service
+enable systemd-readahead-*
+enable systemd-timesyncd.service
+enable systemd-networkd.service
+enable systemd-resolved.service
+
+disable console-getty.service
+disable console-shell.service
+disable debug-shell.service
+
+disable halt.target
+disable kexec.target
+disable poweroff.target
+disable reboot.target
+disable rescue.target
+
+disable syslog.socket
+
+disable systemd-journal-gatewayd.*
+disable systemd-networkd-wait-online.service
diff --git a/system-preset/Makefile b/system-preset/Makefile
new file mode 120000
index 0000000..bd10475
--- /dev/null
+++ b/system-preset/Makefile
@@ -0,0 +1 @@
+../src/Makefile
\ No newline at end of file

commit d7b15e0a0161e8fd823bffd61a4799364871582f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 01:05:39 2014 +0200

    log: don't downgrade log level in non-PID 1 if "quiet" is passed on kernel cmdline
    
    "debug" should apply to all tools, but "quiet" only to PID1.

diff --git a/src/core/main.c b/src/core/main.c
index 3e57f07..4ad3bc2 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -412,11 +412,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 }
 
         } else if (streq(key, "quiet") && !value) {
+
+                log_set_max_level(LOG_NOTICE);
+
                 if (arg_show_status == _SHOW_STATUS_UNSET)
                         arg_show_status = SHOW_STATUS_AUTO;
 
         } else if (streq(key, "debug") && !value) {
+
                 log_set_max_level(LOG_DEBUG);
+
                 if (detect_container(NULL) > 0)
                         log_set_target(LOG_TARGET_CONSOLE);
 
diff --git a/src/shared/log.c b/src/shared/log.c
index 6f17705..9039db3 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -878,9 +878,6 @@ void log_parse_environment(void) {
                         if (l == 5 && startswith(w, "debug")) {
                                 log_set_max_level(LOG_DEBUG);
                                 break;
-                        } else if (l == 5 && startswith(w, "quiet")) {
-                                log_set_max_level(LOG_WARNING);
-                                break;
                         }
                 }
         }

commit 8f294b45cbb627d31342f6a79444be59ce7e2274
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:53:49 2014 +0200

    install: make sure that --root= mode doesn't make us consider all units outside of search path

diff --git a/src/shared/install.c b/src/shared/install.c
index 54fbfe8..26dbbb6 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -47,6 +47,37 @@ typedef struct {
 
 #define _cleanup_install_context_done_ _cleanup_(install_context_done)
 
+static int in_search_path(const char *path, char **search, const char *root_dir) {
+        _cleanup_free_ char *parent = NULL;
+        char **i;
+        int r;
+
+        assert(path);
+
+        r = path_get_parent(path, &parent);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, search) {
+                _cleanup_free_ char *buf = NULL;
+                const char *p;
+
+                if (root_dir) {
+                        buf = strjoin(root_dir, "/", *i, NULL);
+                        if (!buf)
+                                return -ENOMEM;
+
+                        p = buf;
+                } else
+                        p = *i;
+
+                if (path_equal(parent, p))
+                        return 1;
+        }
+
+        return 0;
+}
+
 static int lookup_paths_init_from_scope(LookupPaths *paths,
                                         UnitFileScope scope,
                                         const char *root_dir) {
@@ -746,7 +777,7 @@ int unit_file_link(
                         continue;
                 }
 
-                q = in_search_path(*i, paths.unit_path);
+                q = in_search_path(*i, paths.unit_path, root_dir);
                 if (q < 0)
                         return q;
 
@@ -1308,6 +1339,7 @@ static int install_info_symlink_link(
                 InstallInfo *i,
                 LookupPaths *paths,
                 const char *config_path,
+                const char *root_dir,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
@@ -1320,7 +1352,7 @@ static int install_info_symlink_link(
         assert(config_path);
         assert(i->path);
 
-        r = in_search_path(i->path, paths->unit_path);
+        r = in_search_path(i->path, paths->unit_path, root_dir);
         if (r != 0)
                 return r;
 
@@ -1335,6 +1367,7 @@ static int install_info_apply(
                 InstallInfo *i,
                 LookupPaths *paths,
                 const char *config_path,
+                const char *root_dir,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
@@ -1355,7 +1388,7 @@ static int install_info_apply(
         if (r == 0)
                 r = q;
 
-        q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
+        q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
 
@@ -1395,7 +1428,7 @@ static int install_context_apply(
                 } else if (r >= 0)
                         r += q;
 
-                q = install_info_apply(i, paths, config_path, force, changes, n_changes);
+                q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
                 if (r >= 0 && q < 0)
                         r = q;
         }
diff --git a/src/shared/util.c b/src/shared/util.c
index d840ded..fe05820 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4423,22 +4423,6 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
         return 0;
 }
 
-int in_search_path(const char *path, char **search) {
-        char **i;
-        _cleanup_free_ char *parent = NULL;
-        int r;
-
-        r = path_get_parent(path, &parent);
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH(i, search)
-                if (path_equal(parent, *i))
-                        return 1;
-
-        return 0;
-}
-
 int get_files_in_directory(const char *path, char ***list) {
         _cleanup_closedir_ DIR *d = NULL;
         size_t bufsize = 0, n = 0;
diff --git a/src/shared/util.h b/src/shared/util.h
index 8ddd1a5..73f7c0a 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -543,7 +543,6 @@ int glob_extend(char ***strv, const char *path);
 
 int dirent_ensure_type(DIR *d, struct dirent *de);
 
-int in_search_path(const char *path, char **search);
 int get_files_in_directory(const char *path, char ***list);
 
 char *strjoin(const char *x, ...) _sentinel_;

commit 559367add5e22bf32d14fa1496512c68488f48b0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:33:39 2014 +0200

    install: make sure "systemctl disable foobar at .service" actually removes all instances

diff --git a/src/shared/install.c b/src/shared/install.c
index 1e7863a..54fbfe8 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -266,8 +266,22 @@ static int remove_marked_symlinks_fd(
 
                         if (unit_name_is_instance(de->d_name) &&
                             instance_whitelist &&
-                            !strv_contains(instance_whitelist, de->d_name))
-                                continue;
+                            !strv_contains(instance_whitelist, de->d_name)) {
+
+                                _cleanup_free_ char *w;
+
+                                /* OK, the file is not listed directly
+                                 * in the whitelist, so let's check if
+                                 * the template of it might be
+                                 * listed. */
+
+                                w = unit_name_template(de->d_name);
+                                if (!w)
+                                        return -ENOMEM;
+
+                                if (!strv_contains(instance_whitelist, w))
+                                        continue;
+                        }
 
                         p = path_make_absolute(de->d_name, path);
                         if (!p)
@@ -290,18 +304,14 @@ static int remove_marked_symlinks_fd(
                         if (!found)
                                 continue;
 
-
                         if (unlink(p) < 0 && errno != ENOENT) {
-
                                 if (r == 0)
                                         r = -errno;
                                 continue;
                         }
 
-                        rmdir_parents(p, config_path);
-
                         path_kill_slashes(p);
-
+                        rmdir_parents(p, config_path);
                         add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
 
                         if (!set_get(remove_symlinks_to, p)) {

commit d54c4993699f4fa5feece43715aac3564c4dada6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:15:31 2014 +0200

    install: introduce new DefaultInstance= field for [Install] sections
    
    The DefaultInstance= name is used when enabling template units when only
    specifying the template name, but no instance.
    
    Add DefaultInstance=tty1 to getty at .service, so that when the template
    itself is enabled an instance for tty1 is created.
    
    This is useful so that we "systemctl preset-all" can work properly,
    because we can operate on getty at .service after finding it, and the right
    instance is created.

diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index e903156..b337bb4 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1286,6 +1286,19 @@
                                 of unit names may be
                                 given.</para></listitem>
                         </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>DefaultInstance=</varname></term>
+
+                                <listitem><para>In template unit files
+                                this specifies for which instance the
+                                unit shall be enabled if the template
+                                is enabled without any explicitly set
+                                instance. This option has no effect in
+                                non-template unit files. The specified
+                                string must be usable as instance
+                                identifier.</para></listitem>
+                        </varlistentry>
                 </variablelist>
 
                 <para>The following specifiers are interpreted in the
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 3471ccb..4f3731b 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -319,3 +319,4 @@ Install.Alias,                   NULL,                               0,
 Install.WantedBy,                NULL,                               0,                             0
 Install.RequiredBy,              NULL,                               0,                             0
 Install.Also,                    NULL,                               0,                             0
+Install.DefaultInstance,         NULL,                               0,                             0
diff --git a/src/shared/install.c b/src/shared/install.c
index b300be8..1e7863a 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -819,6 +819,7 @@ static void install_info_free(InstallInfo *i) {
         strv_free(i->aliases);
         strv_free(i->wanted_by);
         strv_free(i->required_by);
+        free(i->default_instance);
         free(i);
 }
 
@@ -911,16 +912,17 @@ static int install_info_add_auto(
                 return install_info_add(c, name_or_path, NULL);
 }
 
-static int config_parse_also(const char *unit,
-                             const char *filename,
-                             unsigned line,
-                             const char *section,
-                             unsigned section_line,
-                             const char *lvalue,
-                             int ltype,
-                             const char *rvalue,
-                             void *data,
-                             void *userdata) {
+static int config_parse_also(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         char *w;
         size_t l;
@@ -947,19 +949,20 @@ static int config_parse_also(const char *unit,
         return 0;
 }
 
-static int config_parse_user(const char *unit,
-                             const char *filename,
-                             unsigned line,
-                             const char *section,
-                             unsigned section_line,
-                             const char *lvalue,
-                             int ltype,
-                             const char *rvalue,
-                             void *data,
-                             void *userdata) {
+static int config_parse_user(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         InstallInfo *i = data;
-        char* printed;
+        char *printed;
         int r;
 
         assert(filename);
@@ -976,6 +979,39 @@ static int config_parse_user(const char *unit,
         return 0;
 }
 
+static int config_parse_default_instance(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        InstallInfo *i = data;
+        char *printed;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = install_full_printf(i, rvalue, &printed);
+        if (r < 0)
+                return r;
+
+        if (!unit_instance_is_valid(printed))
+                return -EINVAL;
+
+        free(i->default_instance);
+        i->default_instance = printed;
+
+        return 0;
+}
+
 static int unit_file_load(
                 InstallContext *c,
                 InstallInfo *info,
@@ -983,12 +1019,13 @@ static int unit_file_load(
                 bool allow_symlink) {
 
         const ConfigTableItem items[] = {
-                { "Install", "Alias",      config_parse_strv, 0, &info->aliases     },
-                { "Install", "WantedBy",   config_parse_strv, 0, &info->wanted_by   },
-                { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
-                { "Install", "Also",       config_parse_also, 0, c                  },
-                { "Exec",    "User",       config_parse_user, 0, info               },
-                { NULL, NULL, NULL, 0, NULL }
+                { "Install", "Alias",           config_parse_strv,             0, &info->aliases           },
+                { "Install", "WantedBy",        config_parse_strv,             0, &info->wanted_by         },
+                { "Install", "RequiredBy",      config_parse_strv,             0, &info->required_by       },
+                { "Install", "DefaultInstance", config_parse_default_instance, 0, info                     },
+                { "Install", "Also",            config_parse_also,             0, c                        },
+                { "Exec",    "User",            config_parse_user,             0, info                     },
+                {}
         };
 
         int fd;
@@ -1009,8 +1046,7 @@ static int unit_file_load(
                 return -ENOMEM;
         }
 
-        r = config_parse(NULL, path, f, NULL,
-                         config_item_table_lookup, (void*) items, true, true, info);
+        r = config_parse(NULL, path, f, NULL, config_item_table_lookup, (void*) items, true, true, info);
         if (r < 0)
                 return r;
 
@@ -1211,54 +1247,30 @@ static int install_info_symlink_alias(
 static int install_info_symlink_wants(
                 InstallInfo *i,
                 const char *config_path,
+                char **list,
+                const char *suffix,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
+        _cleanup_free_ char *buf = NULL;
+        const char *n;
         char **s;
         int r = 0, q;
 
         assert(i);
         assert(config_path);
 
-        STRV_FOREACH(s, i->wanted_by) {
-                _cleanup_free_ char *path = NULL, *dst = NULL;
-
-                q = install_full_printf(i, *s, &dst);
-                if (q < 0)
-                        return q;
-
-                if (!unit_name_is_valid(dst, TEMPLATE_VALID)) {
-                        r = -EINVAL;
-                        continue;
-                }
-
-                if (asprintf(&path, "%s/%s.wants/%s", config_path, dst, i->name) < 0)
+        if (unit_name_is_template(i->name) && i->default_instance) {
+                buf = unit_name_replace_instance(i->name, i->default_instance);
+                if (!buf)
                         return -ENOMEM;
 
-                q = create_symlink(i->path, path, force, changes, n_changes);
-
-                if (r == 0)
-                        r = q;
-        }
-
-        return r;
-}
-
-static int install_info_symlink_requires(
-                InstallInfo *i,
-                const char *config_path,
-                bool force,
-                UnitFileChange **changes,
-                unsigned *n_changes) {
-
-        char **s;
-        int r = 0, q;
-
-        assert(i);
-        assert(config_path);
+                n = buf;
+        } else
+                n = i->name;
 
-        STRV_FOREACH(s, i->required_by) {
+        STRV_FOREACH(s, list) {
                 _cleanup_free_ char *path = NULL, *dst = NULL;
 
                 q = install_full_printf(i, *s, &dst);
@@ -1270,11 +1282,11 @@ static int install_info_symlink_requires(
                         continue;
                 }
 
-                if (asprintf(&path, "%s/%s.requires/%s", config_path, dst, i->name) < 0)
+                path = strjoin(config_path, "/", dst, suffix, n, NULL);
+                if (!path)
                         return -ENOMEM;
 
                 q = create_symlink(i->path, path, force, changes, n_changes);
-
                 if (r == 0)
                         r = q;
         }
@@ -1325,11 +1337,11 @@ static int install_info_apply(
 
         r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
 
-        q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
+        q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
         if (r == 0)
                 r = q;
 
-        q = install_info_symlink_requires(i, config_path, force, changes, n_changes);
+        q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
         if (r == 0)
                 r = q;
 
diff --git a/src/shared/install.h b/src/shared/install.h
index d057bb0..91ce192 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -79,6 +79,8 @@ typedef struct {
         char **aliases;
         char **wanted_by;
         char **required_by;
+
+        char *default_instance;
 } InstallInfo;
 
 int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
diff --git a/units/getty at .service.m4 b/units/getty at .service.m4
index aa853b8..46164ab 100644
--- a/units/getty at .service.m4
+++ b/units/getty at .service.m4
@@ -46,3 +46,4 @@ Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETA
 
 [Install]
 WantedBy=getty.target
+DefaultInstance=tty1

commit e50bd775163cd96be1888943a8785a436be710e8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:13:48 2014 +0200

    install: when looking for a unit file for enabling, search for templates only after traversing all search directories
    
    Let's always make sure to look in all search directories for the full
    unit names first, before looking for templates for them.

diff --git a/src/shared/install.c b/src/shared/install.c
index caad2e5..b300be8 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1035,67 +1035,69 @@ static int unit_file_search(
         assert(paths);
 
         if (info->path) {
-                char *full_path = NULL;
+                const char *path;
 
-                if (!isempty(root_dir))
-                        full_path = strappenda(root_dir, info->path);
+                if (isempty(root_dir))
+                        path = info->path;
+                else
+                        path = strappenda(root_dir, info->path);
 
-                return unit_file_load(c, info, full_path ?: info->path, allow_symlink);
+                return unit_file_load(c, info, path, allow_symlink);
         }
 
         assert(info->name);
 
         STRV_FOREACH(p, paths->unit_path) {
-                _cleanup_free_ char *path = NULL, *full_path = NULL;
+                _cleanup_free_ char *path = NULL;
 
-                path = strjoin(*p, "/", info->name, NULL);
+                if (isempty(root_dir))
+                        path = strjoin(*p, "/", info->name, NULL);
+                else
+                        path = strjoin(root_dir, "/", *p, "/", info->name, NULL);
                 if (!path)
                         return -ENOMEM;
 
-                if (!isempty(root_dir)) {
-                        full_path = strappend(root_dir, path);
-                        if (!full_path)
-                                return -ENOMEM;
-                }
-
-                r = unit_file_load(c, info, full_path ?: path, allow_symlink);
+                r = unit_file_load(c, info, path, allow_symlink);
                 if (r >= 0) {
                         info->path = path;
                         path = NULL;
-                } else if (r == -ENOENT && unit_name_is_instance(info->name)) {
-                        /* Unit file doesn't exist, however instance enablement was requested.
-                         * We will check if it is possible to load template unit file. */
-                        _cleanup_free_ char *template = NULL, *template_dir = NULL;
+                        return r;
+                }
+                if (r != -ENOENT && r != -ELOOP)
+                        return r;
+        }
 
-                        template = unit_name_template(info->name);
-                        if (!template)
-                                return -ENOMEM;
+        if (unit_name_is_instance(info->name)) {
+
+                /* Unit file doesn't exist, however instance
+                 * enablement was requested.  We will check if it is
+                 * possible to load template unit file. */
 
-                        /* We will reuse path variable since we don't need it anymore. */
-                        template_dir = path;
-                        *(strrchr(template_dir, '/') + 1) = '\0';
+                _cleanup_free_ char *template = NULL, *template_dir = NULL;
+
+                template = unit_name_template(info->name);
+                if (!template)
+                        return -ENOMEM;
 
-                        path = strappend(template_dir, template);
+                STRV_FOREACH(p, paths->unit_path) {
+                        _cleanup_free_ char *path = NULL;
+
+                        if (isempty(root_dir))
+                                path = strjoin(*p, "/", template, NULL);
+                        else
+                                path = strjoin(root_dir, "/", *p, "/", template, NULL);
                         if (!path)
                                 return -ENOMEM;
 
-                        if (!isempty(root_dir)) {
-                                free(full_path);
-                                full_path = strappend(root_dir, path);
-                                if (!full_path)
-                                        return -ENOMEM;
-                        }
-
-                        /* Let's try to load template unit. */
-                        r = unit_file_load(c, info, full_path ?: path, allow_symlink);
+                        r = unit_file_load(c, info, path, allow_symlink);
                         if (r >= 0) {
                                 info->path = path;
                                 path = NULL;
+                                return r;
                         }
+                        if (r != -ENOENT && r != -ELOOP)
+                                return r;
                 }
-
-                if (r != -ENOENT && r != -ELOOP)
-                        return r;
         }
 
         return -ENOENT;

commit 1f8c46040e824d7367c2f013de13c87d53f249dc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:11:47 2014 +0200

    install: use symlink_atomic() instead of unlink()+symlink() when force creating a symlink

diff --git a/src/shared/install.c b/src/shared/install.c
index a6a9f19..caad2e5 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -603,13 +603,9 @@ int unit_file_mask(
                                 continue;
 
                         if (force) {
-                                unlink(path);
-
-                                if (symlink("/dev/null", path) >= 0) {
-
+                                if (symlink_atomic("/dev/null", path) >= 0) {
                                         add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
                                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
-
                                         continue;
                                 }
                         }
@@ -770,13 +766,9 @@ int unit_file_link(
                                 continue;
 
                         if (force) {
-                                unlink(path);
-
-                                if (symlink(*i, path) >= 0) {
-
+                                if (symlink_atomic(*i, path) >= 0) {
                                         add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
                                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
-
                                         continue;
                                 }
                         }
@@ -1172,17 +1164,14 @@ static int create_symlink(
         if (!force)
                 return -EEXIST;
 
-        r = unlink(new_path);
-        if (r < 0 && errno != ENOENT)
-                return -errno;
+        r = symlink_atomic(old_path, new_path);
+        if (r < 0)
+                return r;
 
-        if (symlink(old_path, new_path) >= 0) {
-                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
-                add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
-                return 0;
-        }
+        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
+        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
 
-        return -errno;
+        return 0;
 }
 
 static int install_info_symlink_alias(

commit 1dacfd2ad643019f41fb979f5a8c144172911f85
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 17 00:10:54 2014 +0200

    install: various modernizations

diff --git a/src/shared/install.c b/src/shared/install.c
index 5231f32..a6a9f19 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -287,30 +287,31 @@ static int remove_marked_symlinks_fd(
                                 set_get(remove_symlinks_to, dest) ||
                                 set_get(remove_symlinks_to, basename(dest));
 
-                        if (found) {
+                        if (!found)
+                                continue;
 
-                                if (unlink(p) < 0 && errno != ENOENT) {
 
-                                        if (r == 0)
-                                                r = -errno;
-                                        continue;
-                                }
+                        if (unlink(p) < 0 && errno != ENOENT) {
+
+                                if (r == 0)
+                                        r = -errno;
+                                continue;
+                        }
 
-                                rmdir_parents(p, config_path);
+                        rmdir_parents(p, config_path);
 
-                                path_kill_slashes(p);
+                        path_kill_slashes(p);
 
-                                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
 
-                                if (!set_get(remove_symlinks_to, p)) {
+                        if (!set_get(remove_symlinks_to, p)) {
 
-                                        q = mark_symlink_for_removal(&remove_symlinks_to, p);
-                                        if (q < 0) {
-                                                if (r == 0)
-                                                        r = q;
-                                        } else
-                                                *deleted = true;
-                                }
+                                q = mark_symlink_for_removal(&remove_symlinks_to, p);
+                                if (q < 0) {
+                                        if (r == 0)
+                                                r = q;
+                                } else
+                                        *deleted = true;
                         }
                 }
         }
@@ -417,10 +418,8 @@ static int find_symlinks_fd(
 
                         /* This will close nfd, regardless whether it succeeds or not */
                         q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
-
                         if (q > 0)
                                 return 1;
-
                         if (r == 0)
                                 r = q;
 
@@ -595,7 +594,6 @@ int unit_file_mask(
 
                 if (symlink("/dev/null", path) >= 0) {
                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
-
                         continue;
                 }
 
@@ -762,7 +760,6 @@ int unit_file_link(
                         _cleanup_free_ char *dest = NULL;
 
                         q = readlink_and_make_absolute(path, &dest);
-
                         if (q < 0 && errno != ENOENT) {
                                 if (r == 0)
                                         r = q;
@@ -1302,8 +1299,8 @@ static int install_info_symlink_link(
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        int r;
         _cleanup_free_ char *path = NULL;
+        int r;
 
         assert(i);
         assert(paths);
@@ -1314,11 +1311,11 @@ static int install_info_symlink_link(
         if (r != 0)
                 return r;
 
-        if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
+        path = strjoin(config_path, "/", i->name, NULL);
+        if (!path)
                 return -ENOMEM;
 
-        r = create_symlink(i->path, path, force, changes, n_changes);
-        return r;
+        return create_symlink(i->path, path, force, changes, n_changes);
 }
 
 static int install_info_apply(

commit c2a8d7b05c8a067dff701db5127c2c1753e0ac26
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 20:33:29 2014 +0200

    install: teach preset query logic --root= support

diff --git a/src/shared/install.c b/src/shared/install.c
index 7e0e20c..5231f32 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1749,7 +1749,7 @@ UnitFileState unit_file_get_state(
         return r < 0 ? r : state;
 }
 
-int unit_file_query_preset(UnitFileScope scope, const char *name) {
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
         _cleanup_strv_free_ char **files = NULL;
         char **i;
         int r;
@@ -1759,7 +1759,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         assert(name);
 
         if (scope == UNIT_FILE_SYSTEM)
-                r = conf_files_list(&files, ".preset", NULL,
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/system-preset",
                                     "/usr/local/lib/systemd/system-preset",
                                     "/usr/lib/systemd/system-preset",
@@ -1768,7 +1768,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
 #endif
                                     NULL);
         else if (scope == UNIT_FILE_GLOBAL)
-                r = conf_files_list(&files, ".preset", NULL,
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/user-preset",
                                     "/usr/local/lib/systemd/user-preset",
                                     "/usr/lib/systemd/user-preset",
@@ -1780,9 +1780,16 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                 return r;
 
         STRV_FOREACH(i, files) {
+                _cleanup_free_ char *buf = NULL;
                 _cleanup_fclose_ FILE *f;
+                const char *p;
+
+                if (root_dir)
+                        p = buf = strjoin(root_dir, "/", *i, NULL);
+                else
+                        p = *i;
 
-                f = fopen(*i, "re");
+                f = fopen(p, "re");
                 if (!f) {
                         if (errno == ENOENT)
                                 continue;
@@ -1807,15 +1814,19 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                                 l += 6;
                                 l += strspn(l, WHITESPACE);
 
-                                if (fnmatch(l, name, FNM_NOESCAPE) == 0)
+                                if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+                                        log_debug("Preset file says enable %s.", name);
                                         return 1;
+                                }
 
                         } else if (first_word(l, "disable")) {
                                 l += 7;
                                 l += strspn(l, WHITESPACE);
 
-                                if (fnmatch(l, name, FNM_NOESCAPE) == 0)
+                                if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+                                        log_debug("Preset file says disable %s.", name);
                                         return 0;
+                                }
 
                         } else
                                 log_debug("Couldn't parse line '%s'", l);
@@ -1823,6 +1834,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         }
 
         /* Default is "enable" */
+        log_debug("Preset file doesn't say anything about %s, enabling.", name);
         return 1;
 }
 
@@ -1859,7 +1871,7 @@ int unit_file_preset(
                 if (!unit_name_is_valid(*i, TEMPLATE_VALID))
                         return -EINVAL;
 
-                r = unit_file_query_preset(scope, *i);
+                r = unit_file_query_preset(scope, root_dir, *i);
                 if (r < 0)
                         return r;
 
@@ -1966,7 +1978,7 @@ int unit_file_preset_all(
                         if (de->d_type != DT_REG)
                                 continue;
 
-                        r = unit_file_query_preset(scope, de->d_name);
+                        r = unit_file_query_preset(scope, root_dir, de->d_name);
                         if (r < 0)
                                 return r;
 
diff --git a/src/shared/install.h b/src/shared/install.h
index 230cfe1..d057bb0 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -99,7 +99,7 @@ int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
 void unit_file_list_free(Hashmap *h);
 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
 
-int unit_file_query_preset(UnitFileScope scope, const char *name);
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
 
 const char *unit_file_state_to_string(UnitFileState s) _const_;
 UnitFileState unit_file_state_from_string(const char *s) _pure_;

commit d309c1c36426f9a355e28e3c35153281939aeea6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 19:49:31 2014 +0200

    install: beef up preset logic to limit to only enable or only disable, and do all-unit preset operations
    
    The new "systemctl preset-all" command may now be used to put all
    installed units back into the enable/disable state the vendor/admin
    encoded in preset files.
    
    Also, introduce "systemctl --preset-mode=enable-only" and "systemctl
    --preset-mode=disable-only" to only apply the enable or only the disable
    operations of a "systemctl preset" or "systemctl preset-all" operation.
    
    "systemctl preset-all" implements this RFE:
    
    https://bugzilla.redhat.com/show_bug.cgi?id=630174

diff --git a/man/systemctl.xml b/man/systemctl.xml
index e971b34..b388eb3 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -477,6 +477,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       </varlistentry>
 
       <varlistentry>
+        <term><option>--preset-mode=</option></term>
+
+        <listitem>
+          <para>Takes one of <literal>full</literal> (the default),
+          <literal>enable-only</literal>,
+          <literal>disable-only</literal>. When use with the
+          <command>preset</command> or <command>preset-all</command>
+          commands controls whether units shall be disabled and
+          enabled according to the preset rules, or only enabled, or
+          only disabled.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-n</option></term>
         <term><option>--lines=</option></term>
 
@@ -1025,17 +1039,36 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
             command line, to the defaults configured in the preset
             policy files. This has the same effect as
             <command>disable</command> or <command>enable</command>,
-            depending how the unit is listed in the preset files. For
-            more information on the preset policy format, see
+            depending how the unit is listed in the preset files.</para>
+
+            <para>Use <option>--preset-mode=</option> to control
+            whether units shall be enabled and disabled, or only
+            enabled, or only disabled.</para>
+
+            <para>For more information on the preset policy format,
+            see
             <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
             For more information on the concept of presets, please
-            consult the
-            <ulink url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
+            consult the <ulink
+            url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
             document.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
+          <term><command>preset-all</command></term>
+
+          <listitem>
+            <para>Resets all installed unit files to the defaults
+            configured in the preset policy file (see above).</para>
+
+            <para>Use <option>--preset-mode=</option> to control
+            whether units shall be enabled and disabled, or only
+            enabled, or only disabled.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
           <term><command>mask <replaceable>NAME</replaceable>...</command></term>
 
           <listitem>
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 68a68a2..ffef0c7 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1487,8 +1487,8 @@ fail:
 static int method_enable_unit_files_generic(
                 sd_bus *bus,
                 sd_bus_message *message,
-                Manager *m, const
-                char *verb,
+                Manager *m,
+                const char *verb,
                 int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes),
                 bool carries_install_info,
                 sd_bus_error *error) {
@@ -1510,6 +1510,10 @@ static int method_enable_unit_files_generic(
         if (r < 0)
                 return r;
 
+        r = sd_bus_message_read(message, "bb", &runtime, &force);
+        if (r < 0)
+                return r;
+
 #ifdef HAVE_SELINUX
         STRV_FOREACH(i, l) {
                 Unit *u;
@@ -1523,10 +1527,6 @@ static int method_enable_unit_files_generic(
         }
 #endif
 
-        r = sd_bus_message_read(message, "bb", &runtime, &force);
-        if (r < 0)
-                return r;
-
         scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
 
         r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
@@ -1548,14 +1548,74 @@ static int method_link_unit_files(sd_bus *bus, sd_bus_message *message, void *us
         return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_link, false, error);
 }
 
+static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
+        return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes);
+}
+
 static int method_preset_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_preset, true, error);
+        return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_preset_without_mode, true, error);
 }
 
 static int method_mask_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         return method_enable_unit_files_generic(bus, message, userdata, "disable", unit_file_mask, false, error);
 }
 
+static int method_preset_unit_files_with_mode(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+
+        _cleanup_strv_free_ char **l = NULL;
+#ifdef HAVE_SELINUX
+        char **i;
+#endif
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0;
+        Manager *m = userdata;
+        UnitFilePresetMode mm;
+        UnitFileScope scope;
+        int runtime, force, r;
+        const char *mode;
+
+        assert(bus);
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read_strv(message, &l);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force);
+        if (r < 0)
+                return r;
+
+        if (isempty(mode))
+                mm = UNIT_FILE_PRESET_FULL;
+        else {
+                mm = unit_file_preset_mode_from_string(mode);
+                if (mm < 0)
+                        return -EINVAL;
+        }
+
+#ifdef HAVE_SELINUX
+        STRV_FOREACH(i, l) {
+                Unit *u;
+
+                u = manager_get_unit(m, *i);
+                if (u) {
+                        r = selinux_unit_access_check(u, message, "enable", error);
+                        if (r < 0)
+                                return r;
+                }
+        }
+#endif
+
+        scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+
+        r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
+        if (r < 0)
+                return r;
+
+        return reply_unit_file_changes_and_free(m, bus, message, r, changes, n_changes);
+}
+
 static int method_disable_unit_files_generic(
                 sd_bus *bus,
                 sd_bus_message *message,
@@ -1632,6 +1692,44 @@ static int method_set_default_target(sd_bus *bus, sd_bus_message *message, void
         return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
 }
 
+static int method_preset_all_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0;
+        Manager *m = userdata;
+        UnitFilePresetMode mm;
+        UnitFileScope scope;
+        const char *mode;
+        int force, runtime, r;
+
+        assert(bus);
+        assert(message);
+        assert(m);
+
+        r = selinux_access_check(message, "enable", error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force);
+        if (r < 0)
+                return r;
+
+        if (isempty(mode))
+                mm = UNIT_FILE_PRESET_FULL;
+        else {
+                mm = unit_file_preset_mode_from_string(mode);
+                if (mm < 0)
+                        return -EINVAL;
+        }
+
+        scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+
+        r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
+        if (r < 0)
+                return r;
+
+        return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
+}
+
 const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -1716,10 +1814,12 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, 0),
         SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, 0),
         SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, 0),
+        SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, 0),
         SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, 0),
         SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, 0),
         SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, 0),
         SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, 0),
 
         SD_BUS_SIGNAL("UnitNew", "so", 0),
         SD_BUS_SIGNAL("UnitRemoved", "so", 0),
diff --git a/src/shared/install.c b/src/shared/install.c
index 40dc7be..7e0e20c 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1831,12 +1831,12 @@ int unit_file_preset(
                 bool runtime,
                 const char *root_dir,
                 char **files,
+                UnitFilePresetMode mode,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
         _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
-        _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
         _cleanup_free_ char *config_path = NULL;
         char **i;
@@ -1844,6 +1844,7 @@ int unit_file_preset(
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
 
         r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
@@ -1862,25 +1863,141 @@ int unit_file_preset(
                 if (r < 0)
                         return r;
 
-                if (r)
+                if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
                         r = install_info_add_auto(&plus, *i);
-                else
+                else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
                         r = install_info_add_auto(&minus, *i);
+                else
+                        r = 0;
                 if (r < 0)
                         return r;
         }
 
-        r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+        r = 0;
 
-        q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
-        if (r == 0)
-                r = q;
+        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
+                _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
 
-        /* Returns number of symlinks that where supposed to be installed. */
-        q = install_context_apply(&plus, &paths, config_path, root_dir, force,
-                                  changes, n_changes);
-        if (r == 0)
-                r = q;
+                r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+
+                q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
+                if (r == 0)
+                        r = q;
+        }
+
+        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+                /* Returns number of symlinks that where supposed to be installed. */
+                q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+                if (r == 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+int unit_file_preset_all(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                UnitFilePresetMode mode,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_free_ char *config_path = NULL;
+        char **i;
+        int r, q;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
+
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, paths.unit_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                _cleanup_free_ char *buf = NULL;
+                const char *units_dir;
+
+                if (!isempty(root_dir)) {
+                        buf = strjoin(root_dir, "/", *i, NULL);
+                        if (!buf)
+                                return -ENOMEM;
+
+                        units_dir = buf;
+                } else
+                        units_dir = *i;
+
+                d = opendir(units_dir);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                for (;;) {
+                        struct dirent *de;
+
+                        errno = 0;
+                        de = readdir(d);
+                        if (!de && errno != 0)
+                                return -errno;
+
+                        if (!de)
+                                break;
+
+                        if (ignore_file(de->d_name))
+                                continue;
+
+                        if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+                                continue;
+
+                        dirent_ensure_type(d, de);
+
+                        if (de->d_type != DT_REG)
+                                continue;
+
+                        r = unit_file_query_preset(scope, de->d_name);
+                        if (r < 0)
+                                return r;
+
+                        if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
+                                r = install_info_add_auto(&plus, de->d_name);
+                        else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
+                                r = install_info_add_auto(&minus, de->d_name);
+                        else
+                                r = 0;
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        r = 0;
+
+        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
+                _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+
+                r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+
+                q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
+                if (r == 0)
+                        r = q;
+        }
+
+        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+                q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+                if (r == 0)
+                        r = q;
+        }
 
         return r;
 }
@@ -1937,8 +2054,8 @@ int unit_file_get_list(
                 }
 
                 for (;;) {
-                        struct dirent *de;
                         _cleanup_unitfilelist_free_ UnitFileList *f = NULL;
+                        struct dirent *de;
 
                         errno = 0;
                         de = readdir(d);
@@ -2031,3 +2148,11 @@ static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX]
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
+
+static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = {
+        [UNIT_FILE_PRESET_FULL] = "full",
+        [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
+        [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);
diff --git a/src/shared/install.h b/src/shared/install.h
index 5d57b1b..230cfe1 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -45,6 +45,14 @@ typedef enum UnitFileState {
         _UNIT_FILE_STATE_INVALID = -1
 } UnitFileState;
 
+typedef enum UnitFilePresetMode {
+        UNIT_FILE_PRESET_FULL,
+        UNIT_FILE_PRESET_ENABLE_ONLY,
+        UNIT_FILE_PRESET_DISABLE_ONLY,
+        _UNIT_FILE_PRESET_MODE_MAX,
+        _UNIT_FILE_PRESET_INVALID = -1
+} UnitFilePresetMode;
+
 typedef enum UnitFileChangeType {
         UNIT_FILE_SYMLINK,
         UNIT_FILE_UNLINK,
@@ -77,7 +85,8 @@ int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, ch
 int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
 int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
 int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
 int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
 int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
 int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
@@ -97,3 +106,6 @@ UnitFileState unit_file_state_from_string(const char *s) _pure_;
 
 const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_;
 UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_;
+
+const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_;
+UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index b11fee5..56f5084 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -101,6 +101,7 @@ static bool arg_recursive = false;
 static int arg_force = 0;
 static bool arg_ask_password = true;
 static bool arg_runtime = false;
+static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
 static char **arg_wall = NULL;
 static const char *arg_kill_who = NULL;
 static int arg_signal = SIGTERM;
@@ -5209,7 +5210,7 @@ static int enable_unit(sd_bus *bus, char **args) {
                 } else if (streq(verb, "link"))
                         r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
                 else if (streq(verb, "preset")) {
-                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
+                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
                         carries_install_info = r;
                 } else if (streq(verb, "mask"))
                         r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
@@ -5231,7 +5232,7 @@ static int enable_unit(sd_bus *bus, char **args) {
                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
                 int expect_carries_install_info = false;
-                bool send_force = true;
+                bool send_force = true, send_preset_mode = false;
                 const char *method;
 
                 if (streq(verb, "enable")) {
@@ -5246,7 +5247,13 @@ static int enable_unit(sd_bus *bus, char **args) {
                 } else if (streq(verb, "link"))
                         method = "LinkUnitFiles";
                 else if (streq(verb, "preset")) {
-                        method = "PresetUnitFiles";
+
+                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
+                                method = "PresetUnitFilesWithMode";
+                                send_preset_mode = true;
+                        } else
+                                method = "PresetUnitFiles";
+
                         expect_carries_install_info = true;
                 } else if (streq(verb, "mask"))
                         method = "MaskUnitFiles";
@@ -5270,6 +5277,12 @@ static int enable_unit(sd_bus *bus, char **args) {
                 if (r < 0)
                         return bus_log_create_error(r);
 
+                if (send_preset_mode) {
+                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
+                        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);
@@ -5320,6 +5333,61 @@ finish:
         return r;
 }
 
+static int preset_all(sd_bus *bus, char **args) {
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0;
+        int r;
+
+        if (!bus || avoid_bus()) {
+
+                r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
+                if (r < 0) {
+                        log_error("Operation failed: %s", strerror(-r));
+                        goto finish;
+                }
+
+                if (!arg_quiet)
+                        dump_unit_file_changes(changes, n_changes);
+
+                r = 0;
+
+        } else {
+                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "PresetAllUnitFiles",
+                                &error,
+                                &reply,
+                                "sbb",
+                                unit_file_preset_mode_to_string(arg_preset_mode),
+                                arg_runtime,
+                                arg_force);
+                if (r < 0) {
+                        log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+                        return r;
+                }
+
+                r = deserialize_and_dump_unit_file_changes(reply);
+                if (r < 0)
+                        return r;
+
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+                else
+                        r = 0;
+        }
+
+finish:
+        unit_file_changes_free(changes, n_changes);
+
+        return r;
+}
+
 static int unit_is_enabled(sd_bus *bus, char **args) {
 
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -5437,6 +5505,8 @@ static int systemctl_help(void) {
                "     --runtime        Enable unit files only temporarily until next reboot\n"
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
+               "     --preset-mode=   Specifies whether fully apply presets, or only enable,\n"
+               "                      or only disable\n"
                "     --root=PATH      Enable unit files in the specified root directory\n"
                "  -n --lines=INTEGER  Number of journal entries to show\n"
                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
@@ -5477,6 +5547,8 @@ static int systemctl_help(void) {
                "  reenable NAME...                Reenable one or more unit files\n"
                "  preset NAME...                  Enable/disable one or more unit files\n"
                "                                  based on preset configuration\n"
+               "  preset-all                      Enable/disable all unit files based on\n"
+               "                                  preset configuration\n"
                "  is-enabled NAME...              Check whether unit files are enabled\n\n"
                "  mask NAME...                    Mask one or more units\n"
                "  unmask NAME...                  Unmask one or more units\n"
@@ -5625,7 +5697,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_PLAIN,
                 ARG_STATE,
-                ARG_JOB_MODE
+                ARG_JOB_MODE,
+                ARG_PRESET_MODE,
         };
 
         static const struct option options[] = {
@@ -5667,6 +5740,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "plain",               no_argument,       NULL, ARG_PLAIN               },
                 { "state",               required_argument, NULL, ARG_STATE               },
                 { "recursive",           no_argument,       NULL, 'r'                     },
+                { "preset-mode",         required_argument, NULL, ARG_PRESET_MODE         },
                 {}
         };
 
@@ -5932,6 +6006,16 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_recursive = true;
                         break;
 
+                case ARG_PRESET_MODE:
+
+                        arg_preset_mode = unit_file_preset_mode_from_string(optarg);
+                        if (arg_preset_mode < 0) {
+                                log_error("Failed to parse preset mode: %s.", optarg);
+                                return -EINVAL;
+                        }
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -6483,6 +6567,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "is-enabled",            MORE,  2, unit_is_enabled,  NOBUS },
                 { "reenable",              MORE,  2, enable_unit,      NOBUS },
                 { "preset",                MORE,  2, enable_unit,      NOBUS },
+                { "preset-all",            EQUAL, 1, preset_all,       NOBUS },
                 { "mask",                  MORE,  2, enable_unit,      NOBUS },
                 { "unmask",                MORE,  2, enable_unit,      NOBUS },
                 { "link",                  MORE,  2, enable_unit,      NOBUS },
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 2087d52..099eb40 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -253,7 +253,7 @@ int main(int argc, char* argv[]) {
         changes = NULL;
         n_changes = 0;
 
-        r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes);
         assert_se(r >= 0);
 
         dump_changes(changes, n_changes);

commit bcafe923a74e702abbba3655b0270febe143499f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 19:48:31 2014 +0200

    install: simplify and clarify disabling logic for instanced units

diff --git a/src/shared/install.c b/src/shared/install.c
index 115d831..40dc7be 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -194,10 +194,10 @@ static int remove_marked_symlinks_fd(
                 bool *deleted,
                 UnitFileChange **changes,
                 unsigned *n_changes,
-                char** files) {
+                char** instance_whitelist) {
 
-        int r = 0;
         _cleanup_closedir_ DIR *d = NULL;
+        int r = 0;
 
         assert(remove_symlinks_to);
         assert(fd >= 0);
@@ -252,9 +252,8 @@ static int remove_marked_symlinks_fd(
                         }
 
                         /* This will close nfd, regardless whether it succeeds or not */
-                        q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
-
-                        if (r == 0)
+                        q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
+                        if (q < 0 && r == 0)
                                 r = q;
 
                 } else if (de->d_type == DT_LNK) {
@@ -262,6 +261,14 @@ static int remove_marked_symlinks_fd(
                         int q;
                         bool found;
 
+                        if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+                                continue;
+
+                        if (unit_name_is_instance(de->d_name) &&
+                            instance_whitelist &&
+                            !strv_contains(instance_whitelist, de->d_name))
+                                continue;
+
                         p = path_make_absolute(de->d_name, path);
                         if (!p)
                                 return -ENOMEM;
@@ -280,30 +287,29 @@ static int remove_marked_symlinks_fd(
                                 set_get(remove_symlinks_to, dest) ||
                                 set_get(remove_symlinks_to, basename(dest));
 
-                        if (unit_name_is_instance(p))
-                                found = found && strv_contains(files, basename(p));
-
                         if (found) {
 
                                 if (unlink(p) < 0 && errno != ENOENT) {
 
                                         if (r == 0)
                                                 r = -errno;
-                                } else {
-                                        rmdir_parents(p, config_path);
-                                        path_kill_slashes(p);
+                                        continue;
+                                }
+
+                                rmdir_parents(p, config_path);
+
+                                path_kill_slashes(p);
 
-                                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+                                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
 
-                                        if (!set_get(remove_symlinks_to, p)) {
+                                if (!set_get(remove_symlinks_to, p)) {
 
-                                                q = mark_symlink_for_removal(&remove_symlinks_to, p);
-                                                if (q < 0) {
-                                                        if (r == 0)
-                                                                r = q;
-                                                } else
-                                                        *deleted = true;
-                                        }
+                                        q = mark_symlink_for_removal(&remove_symlinks_to, p);
+                                        if (q < 0) {
+                                                if (r == 0)
+                                                        r = q;
+                                        } else
+                                                *deleted = true;
                                 }
                         }
                 }
@@ -317,7 +323,7 @@ static int remove_marked_symlinks(
                 const char *config_path,
                 UnitFileChange **changes,
                 unsigned *n_changes,
-                char** files) {
+                char** instance_whitelist) {
 
         _cleanup_close_ int fd = -1;
         int r = 0;
@@ -343,7 +349,7 @@ static int remove_marked_symlinks(
                 }
 
                 /* This takes possession of cfd and closes it */
-                q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, files);
+                q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
                 if (r == 0)
                         r = q;
         } while (deleted);

commit da39f6a63e9ca59bec23cc9d1cb841703bb5ef56
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 17:19:30 2014 +0200

    install: various modernizations

diff --git a/src/shared/install.c b/src/shared/install.c
index f562063..115d831 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -319,7 +319,8 @@ static int remove_marked_symlinks(
                 unsigned *n_changes,
                 char** files) {
 
-        int fd, r = 0;
+        _cleanup_close_ int fd = -1;
+        int r = 0;
         bool deleted;
 
         assert(config_path);
@@ -347,8 +348,6 @@ static int remove_marked_symlinks(
                         r = q;
         } while (deleted);
 
-        safe_close(fd);
-
         return r;
 }
 
@@ -1830,11 +1829,11 @@ int unit_file_preset(
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
         _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
-        char **i;
-        _cleanup_free_ char *config_path = NULL;
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_free_ char *config_path = NULL;
+        char **i;
         int r, q;
 
         assert(scope >= 0);
@@ -1861,16 +1860,13 @@ int unit_file_preset(
                         r = install_info_add_auto(&plus, *i);
                 else
                         r = install_info_add_auto(&minus, *i);
-
                 if (r < 0)
                         return r;
         }
 
-        r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to,
-                                             config_path, root_dir);
+        r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
 
-        q = remove_marked_symlinks(remove_symlinks_to, config_path,
-                                   changes, n_changes, files);
+        q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
         if (r == 0)
                 r = q;
 
@@ -1899,8 +1895,6 @@ int unit_file_get_list(
 
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
         char **i;
-        _cleanup_free_ char *buf = NULL;
-        _cleanup_closedir_ DIR *d = NULL;
         int r;
 
         assert(scope >= 0);
@@ -1915,22 +1909,19 @@ int unit_file_get_list(
                 return r;
 
         STRV_FOREACH(i, paths.unit_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                _cleanup_free_ char *buf = NULL;
                 const char *units_dir;
 
-                free(buf);
-                buf = NULL;
-
-                if (root_dir) {
-                        if (asprintf(&buf, "%s/%s", root_dir, *i) < 0)
+                if (!isempty(root_dir)) {
+                        buf = strjoin(root_dir, "/", *i, NULL);
+                        if (!buf)
                                 return -ENOMEM;
 
                         units_dir = buf;
                 } else
                         units_dir = *i;
 
-                if (d)
-                        closedir(d);
-
                 d = opendir(units_dir);
                 if (!d) {
                         if (errno == ENOENT)
@@ -1960,15 +1951,9 @@ int unit_file_get_list(
                         if (hashmap_get(h, de->d_name))
                                 continue;
 
-                        r = dirent_ensure_type(d, de);
-                        if (r < 0) {
-                                if (r == -ENOENT)
-                                        continue;
-
-                                return r;
-                        }
+                        dirent_ensure_type(d, de);
 
-                        if (de->d_type != DT_LNK && de->d_type != DT_REG)
+                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
                                 continue;
 
                         f = new0(UnitFileList, 1);

commit e7b4d43ec3d5eb0099a3978f98a46f3c15443b23
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 17:04:49 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index b116451..7a3a5d0 100644
--- a/TODO
+++ b/TODO
@@ -32,6 +32,8 @@ External:
 
 Features:
 
+* tmpfiles: add support for "+" suffix for more commands
+
 * support empty /etc boots nicely:
   - apply presets at first boot
   - tmpfiles: allow overriding of /etc/mtab

commit b9a3302630cce36badb286201031b4cff2da6a74
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 17:04:30 2014 +0200

    unit-name: various modernizations

diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index d0e71f2..a2d62d3 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -28,6 +28,7 @@
 #include "util.h"
 #include "unit-name.h"
 #include "def.h"
+#include "strv.h"
 
 #define VALID_CHARS                             \
         DIGITS LETTERS                          \
@@ -71,9 +72,11 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
          *         string.suffix
          */
 
-        assert(n);
         assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
 
+        if (isempty(n))
+                return false;
+
         if (strlen(n) >= UNIT_NAME_MAX)
                 return false;
 
@@ -105,36 +108,27 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
 }
 
 bool unit_instance_is_valid(const char *i) {
-        assert(i);
 
         /* The max length depends on the length of the string, so we
          * don't really check this here. */
 
-        if (i[0] == 0)
+        if (isempty(i))
                 return false;
 
         /* We allow additional @ in the instance string, we do not
          * allow them in the prefix! */
 
-        for (; *i; i++)
-                if (!strchr("@" VALID_CHARS, *i))
-                        return false;
-
-        return true;
+        return in_charset(i, "@" VALID_CHARS);
 }
 
 bool unit_prefix_is_valid(const char *p) {
 
         /* We don't allow additional @ in the instance string */
 
-        if (p[0] == 0)
+        if (isempty(p))
                 return false;
 
-        for (; *p; p++)
-                if (!strchr(VALID_CHARS, *p))
-                        return false;
-
-        return true;
+        return in_charset(p, VALID_CHARS);
 }
 
 int unit_name_to_instance(const char *n, char **instance) {
@@ -151,15 +145,18 @@ int unit_name_to_instance(const char *n, char **instance) {
                 return 0;
         }
 
-        assert_se(d = strrchr(n, '.'));
-        assert(p < d);
+        d = strrchr(n, '.');
+        if (!d)
+                return -EINVAL;
+        if (d < p)
+                return -EINVAL;
 
         i = strndup(p+1, d-p-1);
         if (!i)
                 return -ENOMEM;
 
         *instance = i;
-        return 0;
+        return 1;
 }
 
 char *unit_name_to_prefix_and_instance(const char *n) {
@@ -168,13 +165,14 @@ char *unit_name_to_prefix_and_instance(const char *n) {
         assert(n);
 
         assert_se(d = strrchr(n, '.'));
-
         return strndup(n, d - n);
 }
 
 char *unit_name_to_prefix(const char *n) {
         const char *p;
 
+        assert(n);
+
         p = strchr(n, '@');
         if (p)
                 return strndup(n, p - n);
@@ -187,7 +185,6 @@ char *unit_name_change_suffix(const char *n, const char *suffix) {
         size_t a, b;
 
         assert(n);
-        assert(unit_name_is_valid(n, TEMPLATE_VALID));
         assert(suffix);
         assert(suffix[0] == '.');
 
@@ -199,16 +196,12 @@ char *unit_name_change_suffix(const char *n, const char *suffix) {
         if (!r)
                 return NULL;
 
-        memcpy(r, n, a);
-        memcpy(r+a, suffix, b+1);
-
+        strcpy(mempcpy(r, n, a), suffix);
         return r;
 }
 
 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
         assert(prefix);
-        assert(unit_prefix_is_valid(prefix));
-        assert(!instance || unit_instance_is_valid(instance));
         assert(suffix);
 
         if (!instance)
@@ -218,10 +211,13 @@ char *unit_name_build(const char *prefix, const char *instance, const char *suff
 }
 
 static char *do_escape_char(char c, char *t) {
+        assert(t);
+
         *(t++) = '\\';
         *(t++) = 'x';
         *(t++) = hexchar(c >> 4);
         *(t++) = hexchar(c);
+
         return t;
 }
 
@@ -250,6 +246,8 @@ static char *do_escape(const char *f, char *t) {
 char *unit_name_escape(const char *f) {
         char *r, *t;
 
+        assert(f);
+
         r = new(char, strlen(f)*4+1);
         if (!r)
                 return NULL;
@@ -294,7 +292,7 @@ char *unit_name_unescape(const char *f) {
 }
 
 char *unit_name_path_escape(const char *f) {
-        _cleanup_free_ char *p;
+        _cleanup_free_ char *p = NULL;
 
         assert(f);
 
@@ -304,14 +302,14 @@ char *unit_name_path_escape(const char *f) {
 
         path_kill_slashes(p);
 
-        if (streq(p, "/") || streq(p, ""))
+        if (STR_IN_SET(p, "/", ""))
                 return strdup("-");
 
         return unit_name_escape(p[0] == '/' ? p + 1 : p);
 }
 
 char *unit_name_path_unescape(const char *f) {
-        char *e;
+        char *e, *w;
 
         assert(f);
 
@@ -320,11 +318,8 @@ char *unit_name_path_unescape(const char *f) {
                 return NULL;
 
         if (e[0] != '/') {
-                char *w;
-
                 w = strappend("/", e);
                 free(e);
-
                 return w;
         }
 
@@ -365,10 +360,11 @@ bool unit_name_is_instance(const char *n) {
 
 char *unit_name_replace_instance(const char *f, const char *i) {
         const char *p, *e;
-        char *r, *k;
+        char *r;
         size_t a, b;
 
         assert(f);
+        assert(i);
 
         p = strchr(f, '@');
         if (!p)
@@ -376,7 +372,7 @@ char *unit_name_replace_instance(const char *f, const char *i) {
 
         e = strrchr(f, '.');
         if (!e)
-                assert_se(e = strchr(f, 0));
+                e = strchr(f, 0);
 
         a = p - f;
         b = strlen(i);
@@ -385,10 +381,7 @@ char *unit_name_replace_instance(const char *f, const char *i) {
         if (!r)
                 return NULL;
 
-        k = mempcpy(r, f, a + 1);
-        k = mempcpy(k, i, b);
-        strcpy(k, e);
-
+        strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
         return r;
 }
 
@@ -397,18 +390,23 @@ char *unit_name_template(const char *f) {
         char *r;
         size_t a;
 
+        assert(f);
+
         p = strchr(f, '@');
         if (!p)
                 return strdup(f);
 
-        assert_se(e = strrchr(f, '.'));
-        a = p - f + 1;
+        e = strrchr(f, '.');
+        if (!e)
+                e = strchr(f, 0);
+
+        a = p - f;
 
-        r = new(char, a + strlen(e) + 1);
+        r = new(char, a + 1 + strlen(e) + 1);
         if (!r)
                 return NULL;
 
-        strcpy(mempcpy(r, f, a), e);
+        strcpy(mempcpy(r, f, a + 1), e);
         return r;
 }
 
@@ -479,15 +477,13 @@ int unit_name_from_dbus_path(const char *path, char **name) {
         return 0;
 }
 
-
 /**
  *  Try to turn a string that might not be a unit name into a
  *  sensible unit name.
  */
 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
+        const char *valid_chars, *f;
         char *r, *t;
-        const char *f;
-        const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
 
         assert(name);
         assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
@@ -501,6 +497,8 @@ char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
         /* We'll only escape the obvious characters here, to play
          * safe. */
 
+        valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
+
         r = new(char, strlen(name) * 4 + strlen(".service") + 1);
         if (!r)
                 return NULL;
@@ -522,7 +520,6 @@ char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
         return r;
 }
 
-
 /**
  *  Similar to unit_name_mangle(), but is called when we know
  *  that this is about a specific unit type.
@@ -532,6 +529,7 @@ char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow
         const char *f;
 
         assert(name);
+        assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
         assert(suffix);
         assert(suffix[0] == '.');
 

commit 6ef9eeed61a291cc42b7d911f5cf5a4deca742a3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 16 17:01:26 2014 +0200

    unit-name: fix detection of unit templates/instances
    
    We need to check for the last dot, not the first one in a unit name, for
    the suffix. Correct that.

diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index 6c167b4..d0e71f2 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -332,7 +332,7 @@ char *unit_name_path_unescape(const char *f) {
 }
 
 bool unit_name_is_template(const char *n) {
-        const char *p;
+        const char *p, *e;
 
         assert(n);
 
@@ -340,11 +340,15 @@ bool unit_name_is_template(const char *n) {
         if (!p)
                 return false;
 
-        return p[1] == '.';
+        e = strrchr(p+1, '.');
+        if (!e)
+                return false;
+
+        return e == p + 1;
 }
 
 bool unit_name_is_instance(const char *n) {
-        const char *p;
+        const char *p, *e;
 
         assert(n);
 
@@ -352,7 +356,11 @@ bool unit_name_is_instance(const char *n) {
         if (!p)
                 return false;
 
-        return p[1] != '.';
+        e = strrchr(p+1, '.');
+        if (!e)
+                return false;
+
+        return e > p + 1;
 }
 
 char *unit_name_replace_instance(const char *f, const char *i) {



More information about the systemd-commits mailing list