[systemd-commits] 20 commits - catalog/systemd.pt_BR.catalog .gitignore Makefile.am po/LINGUAS po/pt_BR.po rules/60-evdev_id.rules src/core src/cryptsetup src/fstab-generator src/network src/shared src/sleep src/test src/udev

Zbigniew Jędrzejewski-Szmek zbyszek at kemper.freedesktop.org
Sun Jan 11 20:50:15 PST 2015


 .gitignore                            |   14 -
 Makefile.am                           |   21 +
 catalog/systemd.pt_BR.catalog         |  264 +++++++++++++++++++++
 po/LINGUAS                            |    1 
 po/pt_BR.po                           |  421 ++++++++++++++++++++++++++++++++++
 rules/60-evdev_id.rules               |   10 
 src/core/load-fragment.c              |    3 
 src/core/manager.c                    |   54 ++--
 src/core/manager.h                    |    3 
 src/core/mount.c                      |   35 +-
 src/core/shutdown.c                   |    4 
 src/core/swap.c                       |   71 -----
 src/cryptsetup/cryptsetup-generator.c |   38 ---
 src/cryptsetup/cryptsetup.c           |    2 
 src/fstab-generator/fstab-generator.c |   63 +----
 src/network/networkd-address.c        |    2 
 src/shared/fstab-util.c               |  150 ++++++++++++
 src/shared/fstab-util.h               |   48 +++
 src/shared/generator.c                |   41 ---
 src/shared/list.h                     |    8 
 src/shared/path-lookup.c              |  128 ++++++----
 src/shared/path-lookup.h              |    2 
 src/shared/util.c                     |  197 ++++++++-------
 src/shared/util.h                     |    4 
 src/sleep/sleep.c                     |    5 
 src/test/test-fstab-util.c            |  138 +++++++++++
 src/test/test-list.c                  |   24 +
 src/test/test-path-lookup.c           |   74 +++++
 src/test/test-unit-file.c             |   25 +-
 src/test/test-util.c                  |   68 ++++-
 src/udev/udev-builtin-evdev_id.c      |   78 ++++++
 src/udev/udev-builtin.c               |    1 
 src/udev/udev.h                       |    2 
 33 files changed, 1603 insertions(+), 396 deletions(-)

New commits:
commit b9e616cc2256501f484f138999ec63a0094f5c4f
Author: Carlos Garnacho <carlosg at gnome.org>
Date:   Sun Jan 11 20:47:19 2015 +0100

    udev: Add builtin/rule to export evdev information as udev properties
    
    This rule is only run on tablet/touchscreen devices, and extracts their size
    in millimeters, as it can be found out through their struct input_absinfo.
    
    The first usecase is exporting device size from tablets/touchscreens. This
    may be useful to separate policy and application at the time of mapping
    these devices to the available outputs in windowing environments that don't
    offer that information as readily (eg. Wayland). This way the compositor can
    stay deterministic, and the mix-and-match heuristics are performed outside.
    
    Conceivably, size/resolution information can be changed through EVIOCSABS
    anywhere else, but we're only interested in values prior to any calibration,
    this rule is thus only run on "add", and no tracking of changes is performed.
    This should only remain a problem if calibration were automatically applied
    by an earlier udev rule (read: don't).
    
      v2: Folded rationale into commit log, made a builtin, set properties
          on device nodes themselves
      v3: Use inline function instead of macro for mm. size calculation,
          use DECIMAL_STR_MAX, other code style issues
      v4: Made rule more selective
      v5: Minor style issues, renamed to a more generic builtin, refined
          rule further.

diff --git a/Makefile.am b/Makefile.am
index 2d45a7b..7f9dc26 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3478,6 +3478,7 @@ dist_udevrules_DATA += \
 	rules/42-usb-hid-pm.rules \
 	rules/50-udev-default.rules \
 	rules/60-drm.rules \
+	rules/60-evdev_id.rules \
 	rules/60-keyboard.rules \
 	rules/70-mouse.rules \
 	rules/60-persistent-storage-tape.rules \
@@ -3561,6 +3562,7 @@ libudev_core_la_SOURCES = \
 	src/udev/udev-builtin.c \
 	src/udev/udev-builtin-btrfs.c \
 	src/udev/udev-builtin-hwdb.c \
+	src/udev/udev-builtin-evdev_id.c \
 	src/udev/udev-builtin-input_id.c \
 	src/udev/udev-builtin-keyboard.c \
 	src/udev/udev-builtin-net_id.c \
diff --git a/rules/60-evdev_id.rules b/rules/60-evdev_id.rules
new file mode 100644
index 0000000..4716a10
--- /dev/null
+++ b/rules/60-evdev_id.rules
@@ -0,0 +1,10 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="evdev_id_end"
+SUBSYSTEM!="input", GOTO="evdev_id_end"
+KERNEL!="event*", GOTO="evdev_id_end"
+
+ENV{ID_INPUT_TOUCHSCREEN}=="1", IMPORT{builtin}="evdev_id"
+ENV{ID_INPUT_TABLET}=="1", IMPORT{builtin}="evdev_id"
+
+LABEL="evdev_id_end"
diff --git a/src/udev/udev-builtin-evdev_id.c b/src/udev/udev-builtin-evdev_id.c
new file mode 100644
index 0000000..1b58bdd
--- /dev/null
+++ b/src/udev/udev-builtin-evdev_id.c
@@ -0,0 +1,78 @@
+/*
+ * evdev_id - extracts miscellaneous information from evdev devices
+ *
+ * Copyright (C) 2014 Red Hat
+ * Author:
+ *   Carlos Garnacho  <carlosg at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with keymap; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <linux/input.h>
+#include "udev.h"
+#include "util.h"
+
+static inline int abs_size_mm(const struct input_absinfo *absinfo) {
+        /* Resolution is defined to be in units/mm for ABS_X/Y */
+        return (absinfo->maximum - absinfo->minimum) / absinfo->resolution;
+}
+
+static void extract_info(struct udev_device *dev, const char *devpath, bool test) {
+        char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)];
+        struct input_absinfo xabsinfo = {}, yabsinfo = {};
+        _cleanup_close_ int fd = -1;
+
+        fd = open(devpath, O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return;
+
+        if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 ||
+            ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0)
+                return;
+
+        if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0)
+                return;
+
+        snprintf(width, sizeof(width), "%d", abs_size_mm(&xabsinfo));
+        snprintf(height, sizeof(height), "%d", abs_size_mm(&yabsinfo));
+
+        udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width);
+        udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height);
+}
+
+static int builtin_evdev_id(struct udev_device *dev, int argc, char *argv[], bool test) {
+        const char *subsystem;
+        const char *devnode;
+
+        subsystem = udev_device_get_subsystem(dev);
+
+        if (!subsystem || !streq(subsystem, "input"))
+                return EXIT_SUCCESS;
+
+        devnode = udev_device_get_devnode(dev);
+        /* not an evdev node */
+        if (!devnode)
+                return EXIT_SUCCESS;
+
+        extract_info(dev, devnode, test);
+
+        return EXIT_SUCCESS;
+}
+
+const struct udev_builtin udev_builtin_evdev_id = {
+        .name = "evdev_id",
+        .cmd = builtin_evdev_id,
+        .help = "evdev devices information",
+};
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 1950ec2..9cfea5e 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -47,6 +47,7 @@ static const struct udev_builtin *builtins[] = {
 #ifdef HAVE_ACL
         [UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
 #endif
+        [UDEV_BUILTIN_EVDEV_ID] = &udev_builtin_evdev_id,
 };
 
 void udev_builtin_init(struct udev *udev) {
diff --git a/src/udev/udev.h b/src/udev/udev.h
index dece6ec..ce4188f 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -164,6 +164,7 @@ enum udev_builtin_cmd {
 #ifdef HAVE_ACL
         UDEV_BUILTIN_UACCESS,
 #endif
+        UDEV_BUILTIN_EVDEV_ID,
         UDEV_BUILTIN_MAX
 };
 struct udev_builtin {
@@ -190,6 +191,7 @@ extern const struct udev_builtin udev_builtin_net_setup_link;
 extern const struct udev_builtin udev_builtin_path_id;
 extern const struct udev_builtin udev_builtin_usb_id;
 extern const struct udev_builtin udev_builtin_uaccess;
+extern const struct udev_builtin udev_builtin_evdev_id;
 void udev_builtin_init(struct udev *udev);
 void udev_builtin_exit(struct udev *udev);
 enum udev_builtin_cmd udev_builtin_lookup(const char *command);

commit b3e486b8194f238fdb7cdf977cb6943305b34b9c
Author: Rafael Ferreira <rafael.f.f1 at gmail.com>
Date:   Sun Jan 11 18:11:42 2015 -0500

    catalog: add pt_BR translation
    
    https://bugs.freedesktop.org/show_bug.cgi?id=88271

diff --git a/Makefile.am b/Makefile.am
index 225938d..2d45a7b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4437,6 +4437,7 @@ dist_catalog_DATA = \
 	catalog/systemd.fr.catalog \
 	catalog/systemd.it.catalog \
 	catalog/systemd.pl.catalog \
+	catalog/systemd.pt_BR.catalog \
 	catalog/systemd.ru.catalog \
 	catalog/systemd.catalog
 
diff --git a/catalog/systemd.pt_BR.catalog b/catalog/systemd.pt_BR.catalog
new file mode 100644
index 0000000..c56fe89
--- /dev/null
+++ b/catalog/systemd.pt_BR.catalog
@@ -0,0 +1,264 @@
+#  This file is part of systemd.
+#
+#  Copyright 2012 Lennart Poettering
+#  Copyright 2015 Rafael Ferreira (translation)
+#
+#  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.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+# Catálogo de mensagens para as mensagens do próprio systemd
+
+# O formato do catálogo está documentado em
+# http://www.freedesktop.org/wiki/Software/systemd/catalog
+
+# Para uma explicação do porquê de fazermos tudo isso, veja
+# https://xkcd.com/1024/
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: O jornal foi inciado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O processo jornal do sistema foi iniciado, arquivos foram abertos e está
+pronto para processar requisições.
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: O jornal foi interrompido
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O processo do jornal do sistema foi desligado e todos os arquivos de jornal
+do sistema foram fechados.
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: Mensagens de um serviço foram suprimidas
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+Um serviço registrou no log um número excessivo de mensagens dentro de um
+período de tempo. Mensagens do serviço foram descartadas.
+
+Note que apenas mensagens de um serviço em questão foram descartadas; outras
+mensagens dos serviços não foram afetadas.
+
+Os limites de quando as mensagens são descartadas pode ser configurado com
+RateLimitInterval= e RateLimitBurst= no
+/etc/systemd/journald.conf. Veja journald.conf(5) para detalhes.
+
+-- e9bf28e6e834481bb6f48f548ad13606
+Subject: Mensagens do jornal foram perdidas
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Mensagens do kernel foram perdidas pois o sistema do jornal não pôde
+processá-las em velocidade suficiente para a demanda.
+
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: Processo @COREDUMP_PID@ (@COREDUMP_COMM@) despejou núcleo
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:core(5)
+
+Processo @COREDUMP_PID@ (@COREDUMP_COMM@) travou e despejou o núcleo.
+
+Isso normalmente indica um erro de programação no programa que travou e
+deveria ser relatado para seu fabricante como um erro.
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: A nova sessão @SESSION_ID@ foi criada para usuário o @USER_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Uma nova sessão com o ID @SESSION_ID@ foi criada para o usuário @USER_ID at .
+
+O processo originador da sessão é @LEADER at .
+
+-- 3354939424b4456d9802ca8333ed424a
+Subject: A sessão @SESSION_ID@ foi terminada
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Um sessão com o ID @SESSION_ID@ foi terminada.
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: Um novo seat @SEAT_ID@ está disponível
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Um novo seat @SEAT_ID@ foi configurado e está disponível.
+
+-- e7852bfe46784ed0accde04bc864c2d5
+Subject: Um seat @SEAT_ID@ foi removido agora
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Um seat @SEAT_ID@ foi removido e não está mais disponível.
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: Time change
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O relógio do sistema foi alterado para @REALTIME@ microssegundos após 1º de
+janeiro de 1970.
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: Fuso horário alterado para @TIMEZONE@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O fuso horário do sistema foi alterado para @TIMEZONE at .
+
+-- b07a249cd024414a82dd00cd181378ff
+Subject: Inicialização do sistema foi concluída
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Todos os serviços do sistema necessários que estão enfileirados para
+executar na inicialização do sistema, foram iniciados com sucesso. Note
+que isso não significa que a máquina está ociosa, pois os serviços podem
+ainda estar ocupados com a inicialização completa.
+
+Inicialização do kernel precisou @KERNEL_USEC@ microssegundos.
+
+Disco de RAM inicial precisou de @INITRD_USEC@ microssegundos.
+
+Inicialização do espaço do usuário precisou de @USERSPACE_USEC@ microssegundos.
+
+-- 6bbd95ee977941e497c48be27c254128
+Subject: Estado de suspensão do sistema @SLEEP@ iniciado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O sistema entrou agora no estado de suspensão @SLEEP at .
+
+-- 8811e6df2a8e40f58a94cea26f8ebf14
+Subject: Estado de suspensão do sistema @SLEEP@ finalizado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O sistema saiu agora do estado de suspensão @SLEEP at .
+
+-- 98268866d1d54a499c4e98921d93bc40
+Subject: Desligamento do sistema iniciado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Desligamento do sistema foi inicializado. O desligamento se iniciou e todos
+os serviços do sistema foram terminados e todos os sistemas desmontados.
+
+-- 7d4958e842da4a758f6c1cdc7b36dcc5
+Subject: Unidade @UNIT@ sendo iniciado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ está sendo iniciada.
+
+-- 39f53479d3a045ac8e11786248231fbf
+Subject: Unidade @UNIT@ concluiu a inicialização
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ concluiu a inicialização.
+
+The start-up result is @RESULT at .
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: Unidade @UNIT@ sendo desligado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ está sendo desligada.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: A unidade @UNIT@ concluiu o desligamento
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ concluiu o desligamento.
+
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: A unidade @UNIT@ falhou
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ falhou.
+
+O resultado é @RESULT at .
+
+-- d34d037fff1847e6ae669a370e694725
+Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ iniciou o recarregamento de sua configuração.
+
+-- 7b05ebc668384222baa8881179cfda54
+Subject: Unidade @UNIT@ concluiu recarregamento de sua configuração
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A unidade @UNIT@ concluiu o recarregamento de sua configuração.
+
+O resultado é @RESULT at .
+
+-- 641257651c1b4ec9a8624d7a40a9e1e7
+Subject: Processo @EXECUTABLE@ não pôde ser executado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O processo @EXECUTABLE@ não pôde ser executado e falhou.
+
+O número de erro retornado enquanto executava este processo foi @ERRNO at .
+
+-- 0027229ca0644181a76c4e92458afa2e
+Subject: Uma ou mais mensagens não puderam ser encaminhadas para o syslog
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Uma ou mais mensagens não puderam ser encaminhadas para o serviço do syslog
+em execução paralela ao journald. Isso normalmente indica que a implementação
+do syslog não foi capaz de se manter com a velocidade das mensagens
+enfileiradas.
+
+-- 1dee0369c7fc4736b7099b38ecb46ee7
+Subject: Ponto de montagem não está vazio
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+O diretório @WHERE@ está especificado como ponto de montagem (o segundo campo
+no /etc/fstab ou campo Where= no arquivo de unidade do systemd) e não está
+vazio.  Isso não interfere com a montagem, mas os arquivos pré-existentes
+neste diretório se tornaram inacessívels. Para ver aqueles arquivos, sobre os
+quais foi realizada a montagem, por favor monte manualmente o sistema de
+arquivos subjacente para uma localização secundária.
+
+-- 24d8d4452573402496068381a6312df2
+Subject: Uma máquina virtual ou contêiner foi iniciado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A máquina virtual @NAME@ com seu PID @LEADER@ incial foi iniciada e está
+pronto para ser usad.
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: Uma máquina virtual ou contêiner foi terminado
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A máquina virtual @NAME@ com seu PID @LEADER@ incial foi desligada.

commit 5701fbbbcfb3492074e2ccaa4431ff8b904aad0b
Author: Rafael Ferreira <rafael.f.f1 at gmail.com>
Date:   Sat Jan 10 13:33:13 2015 -0200

    po: add Brazilian Portuguese translation
    
    https://bugs.freedesktop.org/show_bug.cgi?id=88271

diff --git a/po/LINGUAS b/po/LINGUAS
index 7a9d0f6..2ec9f82 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -4,6 +4,7 @@ fr
 hu
 it
 pl
+pt_BR
 ru
 uk
 sv
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644
index 0000000..5204047
--- /dev/null
+++ b/po/pt_BR.po
@@ -0,0 +1,421 @@
+# Brazilian Portuguese translation for systemd.
+# Copyright (C) 2015 systemd's COPYRIGHT HOLDER
+# This file is distributed under the same license as the systemd package.
+# Rafael Ferreira <rafael.f.f1 at gmail.com>, 2014.
+# Enrico Nicoletto <liverig at gmail.com>, 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: systemd master\n"
+"Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
+"product=systemd&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2015-01-01 21:18+0000\n"
+"PO-Revision-Date: 2015-01-10 12:23-0300\n"
+"Last-Translator: Rafael Ferreira <rafael.f.f1 at gmail.com>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list at gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 1.7.1\n"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+msgid "Set host name"
+msgstr "Definir nome de máquina"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
+msgid "Authentication is required to set the local host name."
+msgstr "É necessária autenticação para definir nome de máquina local."
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+msgid "Set static host name"
+msgstr "Definir nome estático de máquina"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
+msgid ""
+"Authentication is required to set the statically configured local host name, "
+"as well as the pretty host name."
+msgstr ""
+"É necessária autenticação para definir o nome de máquina local configurado "
+"estaticamente, assim como o nome apresentável de máquina."
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+msgid "Set machine information"
+msgstr "Definir informações da máquina"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6
+msgid "Authentication is required to set local machine information."
+msgstr "É necessária autenticação para definir informações de máquina local."
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+msgid "Set system locale"
+msgstr "Definir configurações regionais do sistema"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:2
+msgid "Authentication is required to set the system locale."
+msgstr ""
+"É necessária autenticação para definir as configurações regionais do sistema."
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+msgid "Set system keyboard settings"
+msgstr "Definir configurações de teclado do sistema"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:4
+msgid "Authentication is required to set the system keyboard settings."
+msgstr ""
+"É necessária autenticação para definir as configurações de teclado do "
+"sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:1
+msgid "Allow applications to inhibit system shutdown"
+msgstr "Permitir que aplicativos inibam o desligamento do sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:2
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba o desligamento do "
+"sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:3
+msgid "Allow applications to delay system shutdown"
+msgstr "Permitir que aplicativos atrasem o desligamento do sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:4
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr ""
+"É necessária autenticação para que um aplicativo atrase o desligamento do "
+"sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:5
+msgid "Allow applications to inhibit system sleep"
+msgstr "Permitir que aplicativos inibam a suspensão do sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:6
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a suspensão do "
+"sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:7
+msgid "Allow applications to delay system sleep"
+msgstr "Permite que aplicativos atrasem a suspensão do sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:8
+msgid "Authentication is required for an application to delay system sleep."
+msgstr ""
+"É necessária autenticação para que um aplicativo atrase a suspensão do "
+"sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:9
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr "Permitir que aplicativos inibam a suspensão automática do sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:10
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a suspensão "
+"automática do sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:11
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr ""
+"Permitir que aplicativos inibam o sistema de gerenciar o botão de energia"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:12
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a manipulação do "
+"sistema sobre a chave de ligar/desligar."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:13
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr ""
+"Permitir que aplicativos inibam o sistema de gerenciar o botão de suspensão"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:14
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a manipulação do "
+"sistema sobre a chave de suspensão."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:15
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr ""
+"Permitir que aplicativos inibam o sistema de gerenciar o botão de hibernação"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:16
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a manipulação do "
+"sistema sobre a chave de hibernar."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:17
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr ""
+"Permitir que aplicativos inibam o sistema de gerenciar a abertura/fechamento "
+"da tampa do dispositivo portátil"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:18
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+"É necessária autenticação para que um aplicativo iniba a manipulação do "
+"sistema sobre o interruptor da tela."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in users to run programs"
+msgstr ""
+"Permitir que programas sejam executados por usuários que não possuem sessão"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr ""
+"É necessária autenticação para executar programas como usuário sem sessão "
+"aberta."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
+msgid "Allow attaching devices to seats"
+msgstr "Permitir conectar dispositivos em estações"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
+msgid "Authentication is required for attaching a device to a seat."
+msgstr "É necessária autenticação para conectar um dispositivo em uma estação."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
+msgid "Flush device to seat attachments"
+msgstr "Liberar dispositivo para conexões da estação"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
+msgid ""
+"Authentication is required for resetting how devices are attached to seats."
+msgstr ""
+"É necessária autenticação para redefinir a quantidade de dispositivos "
+"conectados na estação."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
+msgid "Power off the system"
+msgstr "Desligar o sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
+msgid "Authentication is required for powering off the system."
+msgstr "É necessária autenticação para desligar o sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
+msgid "Power off the system while other users are logged in"
+msgstr "Desligar o sistema enquanto outros usuários estão conectados"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:28
+msgid ""
+"Authentication is required for powering off the system while other users are "
+"logged in."
+msgstr ""
+"É necessária autenticação para desligar o sistema enquanto outros usuários "
+"estão conectados."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
+msgid "Power off the system while an application asked to inhibit it"
+msgstr "Desligar o sistema enquanto um aplicativo solicitou inibição"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:30
+msgid ""
+"Authentication is required for powering off the system while an application "
+"asked to inhibit it."
+msgstr ""
+"É necessária autenticação para desligar o sistema enquanto um aplicativo "
+"solicitou inibição."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
+msgid "Reboot the system"
+msgstr "Reiniciar o sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
+msgid "Authentication is required for rebooting the system."
+msgstr "É necessária autenticação para reiniciar o sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
+msgid "Reboot the system while other users are logged in"
+msgstr "Reiniciar o sistema enquanto outros usuários estiverem conectados"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
+msgid ""
+"Authentication is required for rebooting the system while other users are "
+"logged in."
+msgstr ""
+"É necessária autenticação para reiniciar o sistema enquanto outros usuários "
+"estiverem conectados."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
+msgid "Reboot the system while an application asked to inhibit it"
+msgstr "Reiniciar o sistema enquanto um aplicativo solicitou inibição"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:36
+msgid ""
+"Authentication is required for rebooting the system while an application "
+"asked to inhibit it."
+msgstr ""
+"É necessária autenticação para reiniciar o sistema enquanto um aplicativo "
+"solicitou inibição."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
+msgid "Suspend the system"
+msgstr "Suspender o sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
+msgid "Authentication is required for suspending the system."
+msgstr "É necessária autenticação para suspender o sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
+msgid "Suspend the system while other users are logged in"
+msgstr "Suspender o sistema enquanto outros usuários estiverem conectados"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
+msgid ""
+"Authentication is required for suspending the system while other users are "
+"logged in."
+msgstr ""
+"É necessária autenticação para suspender o sistema enquanto outros usuários "
+"estiverem conectados."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
+msgid "Suspend the system while an application asked to inhibit it"
+msgstr "Suspender o sistema enquanto um aplicativo solicitou inibição"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:42
+msgid ""
+"Authentication is required for suspending the system while an application "
+"asked to inhibit it."
+msgstr ""
+"É necessária autenticação para suspender o sistema enquanto um aplicativo "
+"solicitou inibição."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
+msgid "Hibernate the system"
+msgstr "Hibernar o sistema"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
+msgid "Authentication is required for hibernating the system."
+msgstr "É necessária autenticação para hibernar o sistema."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
+msgid "Hibernate the system while other users are logged in"
+msgstr "Hibernar o sistema enquanto outros usuários estiverem conectados"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
+msgid ""
+"Authentication is required for hibernating the system while other users are "
+"logged in."
+msgstr ""
+"É necessária autenticação para hibernar o sistema enquanto outros usuários "
+"estiverem conectados."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
+msgid "Hibernate the system while an application asked to inhibit it"
+msgstr "Hibernar o sistema enquanto um aplicativo solicitou inibição"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:48
+msgid ""
+"Authentication is required for hibernating the system while an application "
+"asked to inhibit it."
+msgstr ""
+"É necessária autenticação para hibernar o sistema enquanto um aplicativo "
+"solicitou inibição."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+msgid "Log into a local container"
+msgstr "Conectar a um contêiner local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:2
+msgid "Authentication is required to log into a local container"
+msgstr "É necessária autenticação para se conectar a um contêiner local."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+msgid "Set system time"
+msgstr "Definir horário do sistema"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2
+msgid "Authentication is required to set the system time."
+msgstr "É necessária autenticação para definir o horário do sistema."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+msgid "Set system timezone"
+msgstr "Definir fuso horário do sistema"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4
+msgid "Authentication is required to set the system timezone."
+msgstr "É necessária autenticação para definir o fuso horário do sistema."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5
+msgid "Set RTC to local timezone or UTC"
+msgstr "Definir o relógio do sistema (RTC) para fuso horário local ou UTC"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
+msgstr ""
+"É necessária autenticação para controlar se o RTC deve, ou não, armazenar o "
+"horário local ou de UTC."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7
+msgid "Turn network time synchronization on or off"
+msgstr "Ligar/desligar a sincronização do horário em rede"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+"É necessária autenticação para controlar se deve ser habilitada, ou não, a "
+"sincronização de horário através de rede."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+msgid "Send passphrase back to system"
+msgstr "Enviar frase secreta de volta ao sistema"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr ""
+"É necessária autenticação para enviar a frase secreta informada de volta ao "
+"sistema."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
+msgid "Manage system services or units"
+msgstr "Gerenciar unidades e serviços do sistema"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
+msgid "Authentication is required to manage system services or units."
+msgstr ""
+"É necessária autenticação para gerenciar unidades e serviços do sistema."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
+msgid "Manage system service or unit files"
+msgstr "Gerenciar arquivos de unidades e serviços do sistema"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
+msgid "Authentication is required to manage system service or unit files."
+msgstr ""
+"É necessária autenticação para gerenciar arquivos \"unit\" e \"service\" do "
+"sistema."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
+msgid "Reload the systemd state"
+msgstr "Recarregar o estado do sistema"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
+msgid "Authentication is required to reload the systemd state."
+msgstr "É necessária autenticação para recarregar o estado do sistema."
+
+#~ msgid "Privileged system and service manager access"
+#~ msgstr "Acesso privilegiado ao gerenciador de serviço e de sistema"

commit a75f4e2a02e287294b21ae9e5b1f28b2f8faea39
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 23:40:46 2015 -0500

    fstab-util: fix priority parsing and add test

diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index a886f6a..cf317e1 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -133,8 +133,10 @@ int fstab_find_pri(const char *options, int *ret) {
         assert(ret);
 
         r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
-        if (r <= 0)
+        if (r < 0)
                 return r;
+        if (r == 0 || !opt)
+                return 0;
 
         r = safe_atou(opt, &pri);
         if (r < 0)
@@ -143,6 +145,6 @@ int fstab_find_pri(const char *options, int *ret) {
         if ((int) pri < 0)
                 return -ERANGE;
 
-        *ret = (int) r;
+        *ret = (int) pri;
         return 1;
 }
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
index 284484d..50e5dee 100644
--- a/src/test/test-fstab-util.c
+++ b/src/test/test-fstab-util.c
@@ -107,6 +107,22 @@ static void test_fstab_filter_options(void) {
         do_fstab_filter_options("", "opt\0", 0, NULL, NULL, "");
 }
 
+static void test_fstab_find_pri(void) {
+        int pri = -1;
+
+        assert_se(fstab_find_pri("pri", &pri) == 0);
+        assert_se(pri == -1);
+
+        assert_se(fstab_find_pri("pri=11", &pri) == 1);
+        assert_se(pri == 11);
+
+        assert_se(fstab_find_pri("opt,pri=12,opt", &pri) == 1);
+        assert_se(pri == 12);
+
+        assert_se(fstab_find_pri("opt,opt,pri=12,pri=13", &pri) == 1);
+        assert_se(pri == 13);
+}
+
 static void test_fstab_yes_no_option(void) {
         assert_se(fstab_test_yes_no_option("nofail,fail,nofail", "nofail\0fail\0") == true);
         assert_se(fstab_test_yes_no_option("nofail,nofail,fail", "nofail\0fail\0") == false);
@@ -117,5 +133,6 @@ static void test_fstab_yes_no_option(void) {
 
 int main(void) {
         test_fstab_filter_options();
+        test_fstab_find_pri();
         test_fstab_yes_no_option();
 }

commit 7f76961982e03d4d5f781e7e7113fc7eff970f82
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 17:21:17 2015 -0500

    shared/util: respect buffer boundary on incomplete escape sequences
    
    cunescape_length_with_prefix() is called with the length as an
    argument, so it cannot rely on the buffer being NUL terminated.
    Move the length check before accessing the memory.
    
    When an incomplete escape sequence was given at the end of the
    buffer, c_l_w_p() would read past the end of the buffer. Fix this
    and add a test.

diff --git a/src/shared/util.c b/src/shared/util.c
index 280e42b..1210900 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1352,12 +1352,19 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                 memcpy(r, prefix, pl);
 
         for (f = s, t = r + pl; f < s + length; f++) {
+                size_t remaining = s + length - f;
+                assert(remaining > 0);
 
-                if (*f != '\\') {
+                if (*f != '\\') {        /* a literal literal */
                         *(t++) = *f;
                         continue;
                 }
 
+                if (--remaining == 0) {  /* copy trailing backslash verbatim */
+                        *(t++) = *f;
+                        break;
+                }
+
                 f++;
 
                 switch (*f) {
@@ -1400,10 +1407,12 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
 
                 case 'x': {
                         /* hexadecimal encoding */
-                        int a, b;
+                        int a = -1, b = -1;
 
-                        a = unhexchar(f[1]);
-                        b = unhexchar(f[2]);
+                        if (remaining >= 2) {
+                                a = unhexchar(f[1]);
+                                b = unhexchar(f[2]);
+                        }
 
                         if (a < 0 || b < 0 || (a == 0 && b == 0)) {
                                 /* Invalid escape code, let's take it literal then */
@@ -1426,11 +1435,13 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                 case '6':
                 case '7': {
                         /* octal encoding */
-                        int a, b, c;
+                        int a = -1, b = -1, c = -1;
 
-                        a = unoctchar(f[0]);
-                        b = unoctchar(f[1]);
-                        c = unoctchar(f[2]);
+                        if (remaining >= 3) {
+                                a = unoctchar(f[0]);
+                                b = unoctchar(f[1]);
+                                c = unoctchar(f[2]);
+                        }
 
                         if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
                                 /* Invalid escape code, let's take it literal then */
@@ -1444,11 +1455,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                         break;
                 }
 
-                case 0:
-                        /* premature end of string. */
-                        *(t++) = '\\';
-                        goto finish;
-
                 default:
                         /* Invalid escape code, let's take it literal then */
                         *(t++) = '\\';
@@ -1457,7 +1463,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                 }
         }
 
-finish:
         *t = 0;
         return r;
 }
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 1e50a29..4bb5154 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -416,8 +416,24 @@ static void test_cescape(void) {
 static void test_cunescape(void) {
         _cleanup_free_ char *unescaped;
 
-        assert_se(unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00"));
-        assert_se(streq(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+        unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00");
+        assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+
+        /* incomplete sequences */
+        unescaped = cunescape("\\x0");
+        assert_se(streq_ptr(unescaped, "\\x0"));
+
+        unescaped = cunescape("\\x");
+        assert_se(streq_ptr(unescaped, "\\x"));
+
+        unescaped = cunescape("\\");
+        assert_se(streq_ptr(unescaped, "\\"));
+
+        unescaped = cunescape("\\11");
+        assert_se(streq_ptr(unescaped, "\\11"));
+
+        unescaped = cunescape("\\1");
+        assert_se(streq_ptr(unescaped, "\\1"));
 }
 
 static void test_foreach_word(void) {

commit e01ff428993f0c126f010b5625002e6a0a8aff4a
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 16:57:02 2015 -0500

    core/load-fragment: avoid allocating 0 bytes when given an invalid command
    
    With a command line like "@/something" we would allocate an array with
    0 elements. Avoid that, and add a test too.

diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index eea4158..242b684 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -589,7 +589,8 @@ int config_parse_exec(const char *unit,
                 }
 
         found:
-                n = new(char*, k + !separate_argv0);
+                /* If seperate_argv0, we'll move first element to path variable */
+                n = new(char*, MAX(k + !separate_argv0, 1u));
                 if (!n)
                         return log_oom();
 
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index d6a7d43..e517f57 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -137,6 +137,20 @@ static void test_config_parse_exec(void) {
         c1 = c1->command_next;
         check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);
 
+        log_info("/* honour_argv0, no args */");
+        r = config_parse_exec(NULL, "fake", 3, "section", 1,
+                              "LValue", 0, "@/RValue",
+                              &c, NULL);
+        assert_se(r == 0);
+        assert_se(c1->command_next == NULL);
+
+        log_info("/* no command, check for bad memory access */");
+        r = config_parse_exec(NULL, "fake", 3, "section", 1,
+                              "LValue", 0, "    ",
+                              &c, NULL);
+        assert_se(r == 0);
+        assert_se(c1->command_next == NULL);
+
         log_info("/* ignore && honour_argv0 */");
         r = config_parse_exec(NULL, "fake", 4, "section", 1,
                               "LValue", 0, "-@/RValue///slashes3 argv0a r1",

commit 4d8629de8bd0f837f99981267d10e71d1f72e47d
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 16:52:50 2015 -0500

    test-unit-file: don't access out-of-bounds memory
    
    Fixes an error introduced by me when the test was added.

diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 6a146a7..d6a7d43 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -73,15 +73,20 @@ static void check_execcommand(ExecCommand *c,
                               const char* argv1,
                               const char* argv2,
                               bool ignore) {
+        size_t n;
+
         assert_se(c);
         log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
                  path, argv0 ?: path, argv1, argv2);
+        n = strv_length(c->argv);
         log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
-                 c->path, c->argv[0], c->argv[1], c->argv[2]);
+                 c->path, c->argv[0], n > 0 ? c->argv[1] : NULL, n > 1 ? c->argv[2] : NULL);
         assert_se(streq(c->path, path));
         assert_se(streq(c->argv[0], argv0 ?: path));
-        assert_se(streq_ptr(c->argv[1], argv1));
-        assert_se(streq_ptr(c->argv[2], argv2));
+        if (n > 0)
+                assert_se(streq_ptr(c->argv[1], argv1));
+        if (n > 1)
+                assert_se(streq_ptr(c->argv[2], argv2));
         assert_se(c->ignore == ignore);
 }
 

commit 17a1c597c5f9be1c25431a764155cd50c0d074b7
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 00:27:37 2015 -0500

    core/mount: filter out noauto,auto,nofail,fail options
    
    We passed the full option string from fstab to /bin/mount. It would in
    turn pass the full option string to its helper, if it needed to invoke
    one. Some helpers would ignore things like "nofail", but others would
    be confused. We could try to get all helpers to ignore those
    "meta-options", but it seems better to simply filter them out.
    
    In our model, /bin/mount simply has no business in knowing whether the
    mount was configured as fail or nofail, auto or noauto, in the
    fstab. If systemd tells invokes a command to mount something, and it
    fails, it should always return an error. It seems cleaner to filter
    out the option, since then there's no doubt how the command should
    behave.
    
    https://bugzilla.redhat.com/show_bug.cgi?id=1177823

diff --git a/src/core/mount.c b/src/core/mount.c
index a551235..9f7c4d2 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -917,6 +917,13 @@ static void mount_enter_mounting(Mount *m) {
                 goto fail;
 
         if (m->from_fragment) {
+                _cleanup_free_ char *opts = NULL;
+
+                r = fstab_filter_options(m->parameters_fragment.options,
+                                         "nofail\0" "fail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
+                if (r < 0)
+                        goto fail;
+
                 r = exec_command_set(m->control_command, "/bin/mount",
                                      m->parameters_fragment.what, m->where, NULL);
                 if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
@@ -925,8 +932,8 @@ static void mount_enter_mounting(Mount *m) {
                         r = exec_command_append(m->control_command, "-s", NULL);
                 if (r >= 0 && m->parameters_fragment.fstype)
                         r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
-                if (r >= 0 && m->parameters_fragment.options)
-                        r = exec_command_append(m->control_command, "-o", m->parameters_fragment.options, NULL);
+                if (r >= 0 && !strempty(opts))
+                        r = exec_command_append(m->control_command, "-o", opts, NULL);
         } else
                 r = -ENOENT;
 

commit b9f111b93f9f442f00266f338b14f25ca8685352
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 00:04:00 2015 -0500

    Support negated fstab options
    
    We would ignore options like "fail" and "auto", and for any option
    which takes a value the first assignment would win. Repeated and
    options equivalent to the default are rarely used, but they have been
    documented forever, and people might use them. Especially on the
    kernel command line it is easier to append a repeated or negated
    option at the end.

diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 3657890..27b2360 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -67,8 +67,8 @@ static int create_disk(
         assert(name);
         assert(device);
 
-        noauto = fstab_test_option(options, "noauto\0");
-        nofail = fstab_test_option(options, "nofail\0");
+        noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
+        nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
         tmp = fstab_test_option(options, "tmp\0");
         swap = fstab_test_option(options, "swap\0");
 
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 15dea7b..e6b37ac 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -69,7 +69,7 @@ static int parse_one_option(const char *option) {
         assert(option);
 
         /* Handled outside of this tool */
-        if (streq(option, "noauto") || streq(option, "nofail"))
+        if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail"))
                 return 0;
 
         if (startswith(option, "cipher=")) {
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 64eed49..bc4c155 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -135,17 +135,15 @@ static int add_swap(
 static bool mount_is_network(struct mntent *me) {
         assert(me);
 
-        return
-                hasmntopt(me, "_netdev") ||
-                fstype_is_network(me->mnt_type);
+        return fstab_test_option(me->mnt_opts, "_netdev\0") ||
+               fstype_is_network(me->mnt_type);
 }
 
 static bool mount_in_initrd(struct mntent *me) {
         assert(me);
 
-        return
-                hasmntopt(me, "x-initrd.mount") ||
-                streq(me->mnt_dir, "/usr");
+        return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") ||
+               streq(me->mnt_dir, "/usr");
 }
 
 static int add_mount(
@@ -344,8 +342,8 @@ static int parse_fstab(bool initrd) {
                 if (is_path(where))
                         path_kill_slashes(where);
 
-                noauto = !!hasmntopt(me, "noauto");
-                nofail = !!hasmntopt(me, "nofail");
+                noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
+                nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
                 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
                           what, where, me->mnt_type,
                           yes_no(noauto), yes_no(nofail));
@@ -356,10 +354,9 @@ static int parse_fstab(bool initrd) {
                         bool automount;
                         const char *post;
 
-                        automount =
-                                  hasmntopt(me, "comment=systemd.automount") ||
-                                  hasmntopt(me, "x-systemd.automount");
-
+                        automount = fstab_test_option(me->mnt_opts,
+                                                      "comment=systemd.automount\0"
+                                                      "x-systemd.automount\0");
                         if (initrd)
                                 post = SPECIAL_INITRD_FS_TARGET;
                         else if (mount_in_initrd(me))
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
index 39ddb71..9f6b32e 100644
--- a/src/shared/fstab-util.h
+++ b/src/shared/fstab-util.h
@@ -23,6 +23,7 @@
 
 #include <stdbool.h>
 #include <stddef.h>
+#include "macro.h"
 
 int fstab_filter_options(const char *opts, const char *names,
                          const char **namefound, char **value, char **filtered);
@@ -32,3 +33,16 @@ static inline bool fstab_test_option(const char *opts, const char *names) {
 }
 
 int fstab_find_pri(const char *options, int *ret);
+
+static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no) {
+        int r;
+        const char *opt;
+
+        /* If first name given is last, return 1.
+         * If second name given is last or neither is found, return 0. */
+
+        r = fstab_filter_options(opts, yes_no, &opt, NULL, NULL);
+        assert(r >= 0);
+
+        return opt == yes_no;
+}
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
index ddf965d..284484d 100644
--- a/src/test/test-fstab-util.c
+++ b/src/test/test-fstab-util.c
@@ -107,6 +107,15 @@ static void test_fstab_filter_options(void) {
         do_fstab_filter_options("", "opt\0", 0, NULL, NULL, "");
 }
 
+static void test_fstab_yes_no_option(void) {
+        assert_se(fstab_test_yes_no_option("nofail,fail,nofail", "nofail\0fail\0") == true);
+        assert_se(fstab_test_yes_no_option("nofail,nofail,fail", "nofail\0fail\0") == false);
+        assert_se(fstab_test_yes_no_option("abc,cde,afail", "nofail\0fail\0") == false);
+        assert_se(fstab_test_yes_no_option("nofail,fail=0,nofail=0", "nofail\0fail\0") == true);
+        assert_se(fstab_test_yes_no_option("nofail,nofail=0,fail=0", "nofail\0fail\0") == false);
+}
+
 int main(void) {
         test_fstab_filter_options();
+        test_fstab_yes_no_option();
 }

commit a6dba97829e345772fae7c1d859e9fe0570ac42b
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sat Jan 10 23:06:52 2015 -0500

    cryptsetup-generator: remove duplicated function

diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 3a866f3..3657890 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -30,6 +30,7 @@
 #include "log.h"
 #include "mkdir.h"
 #include "path-util.h"
+#include "fstab-util.h"
 #include "strv.h"
 #include "unit-name.h"
 #include "util.h"
@@ -50,35 +51,6 @@ static Hashmap *arg_disks = NULL;
 static char *arg_default_options = NULL;
 static char *arg_default_keyfile = NULL;
 
-static bool has_option(const char *haystack, const char *needle) {
-        const char *f = haystack;
-        size_t l;
-
-        assert(needle);
-
-        if (!haystack)
-                return false;
-
-        l = strlen(needle);
-
-        while ((f = strstr(f, needle))) {
-
-                if (f > haystack && f[-1] != ',') {
-                        f++;
-                        continue;
-                }
-
-                if (f[l] != 0 && f[l] != ',') {
-                        f++;
-                        continue;
-                }
-
-                return true;
-        }
-
-        return false;
-}
-
 static int create_disk(
                 const char *name,
                 const char *device,
@@ -95,10 +67,10 @@ static int create_disk(
         assert(name);
         assert(device);
 
-        noauto = has_option(options, "noauto");
-        nofail = has_option(options, "nofail");
-        tmp = has_option(options, "tmp");
-        swap = has_option(options, "swap");
+        noauto = fstab_test_option(options, "noauto\0");
+        nofail = fstab_test_option(options, "nofail\0");
+        tmp = fstab_test_option(options, "tmp\0");
+        swap = fstab_test_option(options, "swap\0");
 
         if (tmp && swap) {
                 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);

commit c5e04d51277994cca29234b33a6b8fc90a183cf3
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sat Jan 10 22:59:44 2015 -0500

    fstab-util: detect out-of-range pri= assignments
    
    We would silently ignore them. One would have to be crazy
    to do assign an out of range value, but simply ignoring it
    bothers me.

diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index 546c81b..a886f6a 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -140,6 +140,9 @@ int fstab_find_pri(const char *options, int *ret) {
         if (r < 0)
                 return r;
 
+        if ((int) pri < 0)
+                return -ERANGE;
+
         *ret = (int) r;
         return 1;
 }

commit 266fd0eabca6b7f10b013c7e4b5c06adc43e1b73
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jan 11 17:35:31 2015 -0500

    .gitignore: add new tests and sort tests alphabetically

diff --git a/.gitignore b/.gitignore
index 2e14b26..2394e7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -154,8 +154,8 @@
 /test-bus-signature
 /test-bus-zero-copy
 /test-calendarspec
-/test-capability
 /test-cap-list
+/test-capability
 /test-catalog
 /test-cgroup
 /test-cgroup-mask
@@ -173,9 +173,7 @@
 /test-dhcp-option
 /test-dhcp-server
 /test-dhcp6-client
-/test-pppoe
 /test-dns-domain
-/test-icmp6-rs
 /test-ellipsize
 /test-engine
 /test-env-replace
@@ -183,8 +181,10 @@
 /test-execute
 /test-fdset
 /test-fileio
+/test-fstab-util
 /test-hashmap
 /test-hostname
+/test-icmp6-rs
 /test-id128
 /test-inhibit
 /test-install
@@ -206,10 +206,9 @@
 /test-libudev
 /test-libudev-sym*
 /test-list
-/test-unaligned
 /test-lldp
-/test-locale-util
 /test-local-addresses
+/test-locale-util
 /test-log
 /test-login
 /test-login-shared
@@ -222,10 +221,12 @@
 /test-network-tables
 /test-ns
 /test-path
+/test-path-lookup
 /test-path-util
+/test-pppoe
 /test-prioq
-/test-ratelimit
 /test-pty
+/test-ratelimit
 /test-replace-var
 /test-resolve
 /test-ring
@@ -248,6 +249,7 @@
 /test-tmpfiles
 /test-udev
 /test-uid-range
+/test-unaligned
 /test-unifont
 /test-unit-file
 /test-unit-name

commit d15d0333be6a1ca7fdd99a1881d967b6be8f387a
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Jan 9 16:58:29 2015 -0500

    Add new function to filter fstab options
    
    This fixes parsing of options in shared/generator.c. Existing code
    had some issues:
    
    - it would treate whitespace and semicolons as seperators. fstab(5)
      is pretty clear that only commas matter. And the syntax does
      not allow for spaces to be inserted in the field in fstab.
      Whitespace might be escaped, but then it should not seperate
      options. Treat whitespace and semicolons as any other character.
    - it assumed that x-systemd.device-timeout would always be followed
      by "=". But this is not guaranteed, hasmntopt will return this
      option even if there's no value. Uninitialized memory could be read.
    - some error paths would log, and inconsistently, some would just
      return an error code.
    
    Filtering is split out to a separate function and tests are added.
    
    Similar code paths in other places are adjusted to use the new function.

diff --git a/Makefile.am b/Makefile.am
index 83d2877..225938d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -769,6 +769,8 @@ libsystemd_shared_la_SOURCES = \
 	src/shared/architecture.h \
 	src/shared/efivars.c \
 	src/shared/efivars.h \
+	src/shared/fstab-util.c \
+	src/shared/fstab-util.h \
 	src/shared/path-util.c \
 	src/shared/path-util.h \
 	src/shared/time-util.c \
@@ -1367,6 +1369,7 @@ tests += \
 	test-calendarspec \
 	test-strip-tab-ansi \
 	test-cgroup-util \
+	test-fstab-util \
 	test-prioq \
 	test-fileio \
 	test-time \
@@ -1609,6 +1612,12 @@ test_fdset_LDADD = \
 	libsystemd-shared.la \
 	libsystemd-internal.la
 
+test_fstab_util_SOURCES = \
+	src/test/test-fstab-util.c
+
+test_fstab_util_LDADD = \
+	libsystemd-shared.la
+
 test_ratelimit_SOURCES = \
 	src/test/test-ratelimit.c
 
diff --git a/src/core/mount.c b/src/core/mount.c
index 13c6348..a551235 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -44,6 +44,7 @@
 #include "bus-common-errors.h"
 #include "exit-status.h"
 #include "def.h"
+#include "fstab-util.h"
 
 #define RETRY_UMOUNT_MAX 32
 
@@ -70,7 +71,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
 static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 
 static bool mount_needs_network(const char *options, const char *fstype) {
-        if (mount_test_option(options, "_netdev"))
+        if (fstab_test_option(options, "_netdev\0"))
                 return true;
 
         if (fstype && fstype_is_network(fstype))
@@ -88,16 +89,10 @@ static bool mount_is_network(const MountParameters *p) {
 static bool mount_is_bind(const MountParameters *p) {
         assert(p);
 
-        if (mount_test_option(p->options, "bind"))
+        if (fstab_test_option(p->options, "bind\0" "rbind\0"))
                 return true;
 
-        if (p->fstype && streq(p->fstype, "bind"))
-                return true;
-
-        if (mount_test_option(p->options, "rbind"))
-                return true;
-
-        if (p->fstype && streq(p->fstype, "rbind"))
+        if (p->fstype && STR_IN_SET(p->fstype, "bind", "rbind"))
                 return true;
 
         return false;
@@ -106,7 +101,7 @@ static bool mount_is_bind(const MountParameters *p) {
 static bool mount_is_auto(const MountParameters *p) {
         assert(p);
 
-        return !mount_test_option(p->options, "noauto");
+        return !fstab_test_option(p->options, "noauto\0");
 }
 
 static bool needs_quota(const MountParameters *p) {
@@ -118,11 +113,8 @@ static bool needs_quota(const MountParameters *p) {
         if (mount_is_bind(p))
                 return false;
 
-        return mount_test_option(p->options, "usrquota") ||
-                mount_test_option(p->options, "grpquota") ||
-                mount_test_option(p->options, "quota") ||
-                mount_test_option(p->options, "usrjquota") ||
-                mount_test_option(p->options, "grpjquota");
+        return fstab_test_option(p->options,
+                                 "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0");
 }
 
 static void mount_init(Unit *u) {
@@ -369,7 +361,7 @@ static bool should_umount(Mount *m) {
                 return false;
 
         p = get_mount_parameters(m);
-        if (p && mount_test_option(p->options, "x-initrd.mount") &&
+        if (p && fstab_test_option(p->options, "x-initrd.mount\0") &&
             !in_initrd())
                 return false;
 
diff --git a/src/core/swap.c b/src/core/swap.c
index cb19d0f..b2ebed4 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -41,6 +41,7 @@
 #include "path-util.h"
 #include "virt.h"
 #include "udev-util.h"
+#include "fstab-util.h"
 
 static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
         [SWAP_DEAD] = UNIT_INACTIVE,
@@ -704,71 +705,6 @@ fail:
         swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
 }
 
-static int mount_find_pri(const char *options, int *ret) {
-        const char *opt;
-        char *end;
-        unsigned long r;
-
-        assert(ret);
-
-        if (!options)
-                return 0;
-
-        opt = mount_test_option(options, "pri");
-        if (!opt)
-                return 0;
-
-        opt += strlen("pri");
-        if (*opt != '=')
-                return -EINVAL;
-
-        errno = 0;
-        r = strtoul(opt + 1, &end, 10);
-        if (errno > 0)
-                return -errno;
-
-        if (end == opt + 1 || (*end != ',' && *end != 0))
-                return -EINVAL;
-
-        *ret = (int) r;
-        return 1;
-}
-
-static int mount_find_discard(const char *options, char **ret) {
-        const char *opt;
-        char *ans;
-        size_t len;
-
-        assert(ret);
-
-        if (!options)
-                return 0;
-
-        opt = mount_test_option(options, "discard");
-        if (!opt)
-                return 0;
-
-        opt += strlen("discard");
-        if (*opt == ',' || *opt == '\0')
-                ans = strdup("all");
-        else {
-                if (*opt != '=')
-                        return -EINVAL;
-
-                len = strcspn(opt + 1, ",");
-                if (len == 0)
-                        return -EINVAL;
-
-                ans = strndup(opt + 1, len);
-        }
-
-        if (!ans)
-                return -ENOMEM;
-
-        *ret = ans;
-        return 1;
-}
-
 static void swap_enter_activating(Swap *s) {
         _cleanup_free_ char *discard = NULL;
         int r, priority = -1;
@@ -779,11 +715,12 @@ static void swap_enter_activating(Swap *s) {
         s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
 
         if (s->from_fragment) {
-                mount_find_discard(s->parameters_fragment.options, &discard);
+                fstab_filter_options(s->parameters_fragment.options, "discard\0",
+                                     NULL, &discard, NULL);
 
                 priority = s->parameters_fragment.priority;
                 if (priority < 0)
-                        mount_find_pri(s->parameters_fragment.options, &priority);
+                        fstab_find_pri(s->parameters_fragment.options, &priority);
         }
 
         r = exec_command_set(s->control_command, "/sbin/swapon", NULL);
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 496657b..64eed49 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -29,6 +29,7 @@
 #include "util.h"
 #include "unit-name.h"
 #include "path-util.h"
+#include "fstab-util.h"
 #include "mount-setup.h"
 #include "special.h"
 #include "mkdir.h"
@@ -47,33 +48,6 @@ static char *arg_usr_what = NULL;
 static char *arg_usr_fstype = NULL;
 static char *arg_usr_options = NULL;
 
-static int mount_find_pri(struct mntent *me, int *ret) {
-        char *end, *opt;
-        unsigned long r;
-
-        assert(me);
-        assert(ret);
-
-        opt = hasmntopt(me, "pri");
-        if (!opt)
-                return 0;
-
-        opt += strlen("pri");
-        if (*opt != '=')
-                return -EINVAL;
-
-        errno = 0;
-        r = strtoul(opt + 1, &end, 10);
-        if (errno > 0)
-                return -errno;
-
-        if (end == opt + 1 || (*end != ',' && *end != 0))
-                return -EINVAL;
-
-        *ret = (int) r;
-        return 1;
-}
-
 static int add_swap(
                 const char *what,
                 struct mntent *me,
@@ -97,11 +71,9 @@ static int add_swap(
                 return 0;
         }
 
-        r = mount_find_pri(me, &pri);
-        if (r < 0) {
-                log_error("Failed to parse priority");
-                return r;
-        }
+        r = fstab_find_pri(me->mnt_opts, &pri);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse priority: %m");
 
         name = unit_name_from_path(what, ".swap");
         if (!name)
@@ -434,8 +406,7 @@ static int add_root_mount(void) {
         if (!arg_root_options)
                 opts = arg_root_rw > 0 ? "rw" : "ro";
         else if (arg_root_rw >= 0 ||
-                 (!mount_test_option(arg_root_options, "ro") &&
-                  !mount_test_option(arg_root_options, "rw")))
+                 !fstab_test_option(arg_root_options, "ro\0" "rw\0"))
                 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
         else
                 opts = arg_root_options;
@@ -492,8 +463,7 @@ static int add_usr_mount(void) {
 
         if (!arg_usr_options)
                 opts = arg_root_rw > 0 ? "rw" : "ro";
-        else if (!mount_test_option(arg_usr_options, "ro") &&
-                 !mount_test_option(arg_usr_options, "rw"))
+        else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
                 opts = strappenda(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
         else
                 opts = arg_usr_options;
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
new file mode 100644
index 0000000..546c81b
--- /dev/null
+++ b/src/shared/fstab-util.c
@@ -0,0 +1,145 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Zbigniew Jędrzejewski-Szmek
+
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fstab-util.h"
+#include "strv.h"
+#include "util.h"
+
+int fstab_filter_options(const char *opts, const char *names,
+                         const char **namefound, char **value, char **filtered) {
+        const char *name, *n = NULL, *x;
+        _cleanup_strv_free_ char **stor = NULL;
+        _cleanup_free_ char *v = NULL, **strv = NULL;
+
+        assert(names && *names);
+
+        if (!opts)
+                goto answer;
+
+        /* If !value and !filtered, this function is not allowed to fail. */
+
+        if (!filtered) {
+                const char *word, *state;
+                size_t l;
+
+                FOREACH_WORD_SEPARATOR(word, l, opts, ",", state)
+                        NULSTR_FOREACH(name, names) {
+                                if (l < strlen(name))
+                                        continue;
+                                if (!strneq(word, name, strlen(name)))
+                                        continue;
+
+                                /* we know that the string is NUL
+                                 * terminated, so *x is valid */
+                                x = word + strlen(name);
+                                if (IN_SET(*x, '\0', '=', ',')) {
+                                        n = name;
+                                        if (value) {
+                                                free(v);
+                                                if (IN_SET(*x, '\0', ','))
+                                                        v = NULL;
+                                                else {
+                                                        assert(*x == '=');
+                                                        x++;
+                                                        v = strndup(x, l - strlen(name) - 1);
+                                                        if (!v)
+                                                                return -ENOMEM;
+                                                }
+                                        }
+                                }
+                        }
+        } else {
+                char **t, **s;
+
+                stor = strv_split(opts, ",");
+                if (!stor)
+                        return -ENOMEM;
+                strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
+                if (!strv)
+                        return -ENOMEM;
+
+                for (s = t = strv; *s; s++) {
+                        NULSTR_FOREACH(name, names) {
+                                x = startswith(*s, name);
+                                if (x && IN_SET(*x, '\0', '='))
+                                        goto found;
+                        }
+
+                        *t = *s;
+                        t++;
+                        continue;
+                found:
+                        /* Keep the last occurence found */
+                        n = name;
+                        if (value) {
+                                free(v);
+                                if (*x == '\0')
+                                        v = NULL;
+                                else {
+                                        assert(*x == '=');
+                                        x++;
+                                        v = strdup(x);
+                                        if (!v)
+                                                return -ENOMEM;
+                                }
+                        }
+                }
+                *t = NULL;
+        }
+
+answer:
+        if (namefound)
+                *namefound = n;
+        if (filtered) {
+                char *f;
+
+                f = strv_join(strv, ",");
+                if (!f)
+                        return -ENOMEM;
+
+                *filtered = f;
+        }
+        if (value) {
+                *value = v;
+                v = NULL;
+        }
+
+        return !!n;
+}
+
+int fstab_find_pri(const char *options, int *ret) {
+        _cleanup_free_ char *opt = NULL;
+        int r;
+        unsigned pri;
+
+        assert(ret);
+
+        r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
+        if (r <= 0)
+                return r;
+
+        r = safe_atou(opt, &pri);
+        if (r < 0)
+                return r;
+
+        *ret = (int) r;
+        return 1;
+}
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
new file mode 100644
index 0000000..39ddb71
--- /dev/null
+++ b/src/shared/fstab-util.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Zbigniew Jędrzejewski-Szmek
+
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stddef.h>
+
+int fstab_filter_options(const char *opts, const char *names,
+                         const char **namefound, char **value, char **filtered);
+
+static inline bool fstab_test_option(const char *opts, const char *names) {
+        return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
+}
+
+int fstab_find_pri(const char *options, int *ret);
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 465e5f6..4140afb 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -28,6 +28,7 @@
 #include "unit-name.h"
 #include "generator.h"
 #include "path-util.h"
+#include "fstab-util.h"
 #include "dropin.h"
 
 int generator_write_fsck_deps(
@@ -92,42 +93,16 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
          * endless device timeouts for devices that show up only after
          * user input, like crypto devices. */
 
-        _cleanup_free_ char *node = NULL, *unit = NULL, *t = NULL;
-        char *start, *timeout;
+        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
         usec_t u;
         int r;
-        size_t len;
-
-        if ((start = mount_test_option(opts, "comment=systemd.device-timeout")))
-                timeout = start + 31;
-        else if ((start = mount_test_option(opts, "x-systemd.device-timeout")))
-                timeout = start + 25;
-        else {
-                if (filtered) {
-                        *filtered = strdup(opts ?: "");
-                        if (!*filtered)
-                                return log_oom();
-                }
 
-                return 0;
-        }
-
-        len = strcspn(timeout, ",;" WHITESPACE);
-        t = strndup(timeout, len);
-        if (!t)
-                return -ENOMEM;
-
-        if (filtered) {
-                char *prefix, *postfix;
+        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" "x-systemd.device-timeout\0",
+                                 NULL, &timeout, filtered);
+        if (r <= 0)
+                return r;
 
-                prefix = strndupa(opts, start - opts - (start != opts));
-                postfix = timeout + len + (start == opts && timeout[len] != '\0');
-                *filtered = strjoin(prefix, *postfix ? postfix : NULL, NULL);
-                if (!*filtered)
-                        return log_oom();
-        }
-
-        r = parse_sec(t, &u);
+        r = parse_sec(timeout, &u);
         if (r < 0) {
                 log_warning("Failed to parse timeout for %s, ignoring: %s",
                             where, timeout);
@@ -140,7 +115,7 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
 
         unit = unit_name_from_path(node, ".device");
         if (!unit)
-                return -ENOMEM;
+                return log_oom();
 
         return write_drop_in_format(dir, unit, 50, "device-timeout",
                                     "# Automatically generated by %s\n\n"
diff --git a/src/shared/util.c b/src/shared/util.c
index 6520e51..280e42b 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -6744,23 +6744,6 @@ uint64_t physical_memory(void) {
         return (uint64_t) mem * (uint64_t) page_size();
 }
 
-char* mount_test_option(const char *haystack, const char *needle) {
-
-        struct mntent me = {
-                .mnt_opts = (char*) haystack
-        };
-
-        assert(needle);
-
-        /* Like glibc's hasmntopt(), but works on a string, not a
-         * struct mntent */
-
-        if (!haystack)
-                return NULL;
-
-        return hasmntopt(&me, needle);
-}
-
 void hexdump(FILE *f, const void *p, size_t s) {
         const uint8_t *b = p;
         unsigned n = 0;
diff --git a/src/shared/util.h b/src/shared/util.h
index 141a3fe..b337249 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -1010,8 +1010,6 @@ const char *personality_to_string(unsigned long);
 
 uint64_t physical_memory(void);
 
-char* mount_test_option(const char *haystack, const char *needle);
-
 void hexdump(FILE *f, const void *p, size_t s);
 
 union file_handle_union {
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
new file mode 100644
index 0000000..ddf965d
--- /dev/null
+++ b/src/test/test-fstab-util.c
@@ -0,0 +1,112 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Zbigniew Jędrzejewski-Szmek
+
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fstab-util.h"
+#include "util.h"
+#include "log.h"
+
+/*
+int fstab_filter_options(const char *opts, const char *names,
+                         const char **namefound, char **value, char **filtered);
+*/
+
+static void do_fstab_filter_options(const char *opts,
+                                      const char *remove,
+                                      int r_expected,
+                                      const char *name_expected,
+                                      const char *value_expected,
+                                      const char *filtered_expected) {
+
+        int r;
+        const char *name;
+        _cleanup_free_ char *value, *filtered;
+
+        r = fstab_filter_options(opts, remove, &name, &value, &filtered);
+        log_info("\"%s\" → %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"",
+                 opts, r, name, value, filtered,
+                 r_expected, name_expected, value_expected, filtered_expected ?: opts);
+        assert_se(r == r_expected);
+        assert_se(streq_ptr(name, name_expected));
+        assert_se(streq_ptr(value, value_expected));
+        assert_se(streq_ptr(filtered, filtered_expected ?: opts));
+
+        /* also test the malloc-less mode */
+        r = fstab_filter_options(opts, remove, &name, NULL, NULL);
+        log_info("\"%s\" → %d, \"%s\", expected %d, \"%s\"",
+                 opts, r, name,
+                 r_expected, name_expected);
+        assert_se(r == r_expected);
+        assert_se(streq_ptr(name, name_expected));
+}
+
+static void test_fstab_filter_options(void) {
+        do_fstab_filter_options("opt=0", "opt\0x-opt\0", 1, "opt", "0", "");
+        do_fstab_filter_options("opt=0", "x-opt\0opt\0", 1, "opt", "0", "");
+        do_fstab_filter_options("opt", "opt\0x-opt\0", 1, "opt", NULL, "");
+        do_fstab_filter_options("opt", "x-opt\0opt\0", 1, "opt", NULL, "");
+        do_fstab_filter_options("x-opt", "x-opt\0opt\0", 1, "x-opt", NULL, "");
+
+        do_fstab_filter_options("opt=0,other", "opt\0x-opt\0", 1, "opt", "0", "other");
+        do_fstab_filter_options("opt=0,other", "x-opt\0opt\0", 1, "opt", "0", "other");
+        do_fstab_filter_options("opt,other", "opt\0x-opt\0", 1, "opt", NULL, "other");
+        do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, "opt", NULL, "other");
+        do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, "x-opt", NULL, "other");
+
+        do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
+        do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
+        do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
+
+        do_fstab_filter_options("first,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first");
+        do_fstab_filter_options("first=1,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first=1");
+        do_fstab_filter_options("first,opt=", "opt\0x-opt\0", 1, "opt", "", "first");
+        do_fstab_filter_options("first=1,opt", "opt\0x-opt\0", 1, "opt", NULL, "first=1");
+        do_fstab_filter_options("first=1,x-opt", "opt\0x-opt\0", 1, "x-opt", NULL, "first=1");
+
+        do_fstab_filter_options("first,opt=0,last=1", "opt\0x-opt\0", 1, "opt", "0", "first,last=1");
+        do_fstab_filter_options("first=1,opt=0,last=2", "x-opt\0opt\0", 1, "opt", "0", "first=1,last=2");
+        do_fstab_filter_options("first,opt,last", "opt\0", 1, "opt", NULL, "first,last");
+        do_fstab_filter_options("first=1,opt,last", "x-opt\0opt\0", 1, "opt", NULL, "first=1,last");
+        do_fstab_filter_options("first=,opt,last", "opt\0noopt\0", 1, "opt", NULL, "first=,last");
+
+        /* check repeated options */
+        do_fstab_filter_options("first,opt=0,noopt=1,last=1", "opt\0noopt\0", 1, "noopt", "1", "first,last=1");
+        do_fstab_filter_options("first=1,opt=0,last=2,opt=1", "opt\0", 1, "opt", "1", "first=1,last=2");
+        do_fstab_filter_options("x-opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", "");
+        do_fstab_filter_options("opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", "");
+
+        /* check that semicolons are not misinterpreted */
+        do_fstab_filter_options("opt=0;", "opt\0", 1, "opt", "0;", "");
+        do_fstab_filter_options("opt;=0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL);
+        do_fstab_filter_options("opt;", "opt\0x-opt\0", 0, NULL, NULL, NULL);
+
+        /* check that spaces are not misinterpreted */
+        do_fstab_filter_options("opt=0 ", "opt\0", 1, "opt", "0 ", "");
+        do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL);
+        do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, NULL, NULL, NULL);
+
+        /* check function will NULL args */
+        do_fstab_filter_options(NULL, "opt\0", 0, NULL, NULL, "");
+        do_fstab_filter_options("", "opt\0", 0, NULL, NULL, "");
+}
+
+int main(void) {
+        test_fstab_filter_options();
+}

commit 1e39ff926f815d241721ed9486945cf599ef8c2f
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Jan 9 13:34:37 2015 -0500

    network: apply static addresses in specified order
    
    https://bugs.freedesktop.org/show_bug.cgi?id=83270

diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index a32e870..b28c2e0 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -59,7 +59,7 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
 
         address->network = network;
 
-        LIST_PREPEND(addresses, network->static_addresses, address);
+        LIST_APPEND(addresses, network->static_addresses, address);
 
         if (section) {
                 address->section = section;

commit 502f1733e320b1339beafa949a41db3027c46ec6
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Jan 9 13:34:01 2015 -0500

    shared/list: add LIST_APPEND

diff --git a/src/shared/list.h b/src/shared/list.h
index c020f7e..f0458b5 100644
--- a/src/shared/list.h
+++ b/src/shared/list.h
@@ -55,6 +55,14 @@
                 *_head = _item;                                         \
         } while(false)
 
+/* Append an item to the list */
+#define LIST_APPEND(name,head,item)                                     \
+        do {                                                            \
+                typeof(*(head)) *_tail;                                 \
+                LIST_FIND_TAIL(name,head,_tail);                        \
+                LIST_INSERT_AFTER(name,head,_tail,item);                \
+        } while(false)
+
 /* Remove an item from the list */
 #define LIST_REMOVE(name,head,item)                                     \
         do {                                                            \
diff --git a/src/test/test-list.c b/src/test/test-list.c
index fa52ad1..e9d47f0 100644
--- a/src/test/test-list.c
+++ b/src/test/test-list.c
@@ -105,5 +105,29 @@ int main(int argc, const char *argv[]) {
         LIST_REMOVE(item, head, &items[3]);
         assert_se(LIST_JUST_US(item, &items[3]));
 
+        assert_se(head == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items); i++) {
+                assert_se(LIST_JUST_US(item, &items[i]));
+                LIST_APPEND(item, head, &items[i]);
+        }
+
+        assert_se(!LIST_JUST_US(item, head));
+
+        assert_se(items[0].item_next == &items[1]);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[2].item_next == &items[3]);
+        assert_se(items[3].item_next == NULL);
+
+        assert_se(items[0].item_prev == NULL);
+        assert_se(items[1].item_prev == &items[0]);
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[3].item_prev == &items[2]);
+
+        for (i = 0; i < ELEMENTSOF(items); i++)
+          LIST_REMOVE(item, head, &items[i]);
+
+        assert_se(head == NULL);
+
         return 0;
 }

commit 33e1e5a756300e29c74fdc59ea762f9394df8368
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Jan 8 23:34:21 2015 -0500

    path-lookup: allow /run to override /etc in generator search
    
    Generators are different than unit files: they are never automatically
    generated, so there's no point in allowing /etc to override /run. On
    the other hand, overriding /etc might be useful in some cases.

diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 1e5bb85..291a2f4 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -222,14 +222,14 @@ static char** user_dirs(
 
 char **generator_paths(SystemdRunningAs running_as) {
         if (running_as == SYSTEMD_USER)
-                return strv_new("/etc/systemd/user-generators",
-                                "/run/systemd/user-generators",
+                return strv_new("/run/systemd/user-generators",
+                                "/etc/systemd/user-generators",
                                 "/usr/local/lib/systemd/user-generators",
                                 USER_GENERATOR_PATH,
                                 NULL);
         else
-                return strv_new("/etc/systemd/system-generators",
-                                "/run/systemd/system-generators",
+                return strv_new("/run/systemd/system-generators",
+                                "/etc/systemd/system-generators",
                                 "/usr/local/lib/systemd/system-generators",
                                 SYSTEM_GENERATOR_PATH,
                                 NULL);

commit 4f1a33dd0d62171bbeac74859642b8ff0ab547b8
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Jan 8 23:29:33 2015 -0500

    test-path-lookup: add simple test for path lookup functions

diff --git a/Makefile.am b/Makefile.am
index 8a60f3f..83d2877 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1354,6 +1354,7 @@ tests += \
 	test-utf8 \
 	test-ellipsize \
 	test-util \
+	test-path-lookup \
 	test-ring \
 	test-barrier \
 	test-pty \
@@ -1620,6 +1621,14 @@ test_util_SOURCES = \
 test_util_LDADD = \
 	libsystemd-shared.la
 
+test_path_lookup_SOURCES = \
+	src/test/test-path-lookup.c
+
+test_path_lookup_LDADD = \
+	-lm \
+	libsystemd-units.la \
+	libsystemd-shared.la
+
 test_uid_range_SOURCES = \
 	src/test/test-uid-range.c
 
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
new file mode 100644
index 0000000..a6ce8c9
--- /dev/null
+++ b/src/test/test-path-lookup.c
@@ -0,0 +1,74 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+  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.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "path-lookup.h"
+#include "log.h"
+#include "strv.h"
+
+static void test_paths(SystemdRunningAs running_as, bool personal) {
+        char template[] = "/tmp/test-path-lookup.XXXXXXX";
+
+        _cleanup_lookup_paths_free_ LookupPaths lp = {};
+        char *exists, *not;
+
+        assert_se(mkdtemp(template));
+        exists = strappenda(template, "/exists");
+        assert_se(mkdir(exists, 0755) == 0);
+        not = strappenda(template, "/not");
+
+        assert_se(lookup_paths_init(&lp, running_as, personal, NULL, exists, not, not) == 0);
+
+        assert_se(!strv_isempty(lp.unit_path));
+        assert_se(strv_contains(lp.unit_path, exists));
+        assert_se(strv_contains(lp.unit_path, not));
+
+        assert_se(rm_rf_dangerous(template, false, true, false) >= 0);
+}
+
+static void print_generator_paths(SystemdRunningAs running_as) {
+        _cleanup_strv_free_ char **paths;
+        char **dir;
+
+        log_info("Generators dirs (%s):", running_as == SYSTEMD_SYSTEM ? "system" : "user");
+
+        paths = generator_paths(running_as);
+        STRV_FOREACH(dir, paths)
+                log_info("        %s", *dir);
+}
+
+int main(int argc, char **argv) {
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_paths(SYSTEMD_SYSTEM, false);
+        test_paths(SYSTEMD_SYSTEM, true);
+        test_paths(SYSTEMD_USER, false);
+        test_paths(SYSTEMD_USER, true);
+
+        print_generator_paths(SYSTEMD_SYSTEM);
+        print_generator_paths(SYSTEMD_USER);
+
+        return EXIT_SUCCESS;
+}

commit aac7766c107273abdc5d6f52f9a9f88d18cb14b3
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Jan 8 21:21:12 2015 -0500

    test-util: make sure that masking and overriding works

diff --git a/src/test/test-util.c b/src/test/test-util.c
index 1a59978..1e50a29 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1207,28 +1207,47 @@ static void test_glob_exists(void) {
 }
 
 static void test_execute_directory(void) {
-        char template[] = "/tmp/test-readlink_and_make_absolute.XXXXXXX";
-        const char const* dirs[] = {template, NULL};
-        const char *name, *name2, *name3;
-
-        assert_se(mkdtemp(template));
-
-        name = strappenda(template, "/script");
-        name2 = strappenda(template, "/script2");
-        name3 = strappenda(template, "/useless");
+        char template_lo[] = "/tmp/test-readlink_and_make_absolute-lo.XXXXXXX";
+        char template_hi[] = "/tmp/test-readlink_and_make_absolute-hi.XXXXXXX";
+        const char const* dirs[] = {template_hi, template_lo, NULL};
+        const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
+
+        assert_se(mkdtemp(template_lo));
+        assert_se(mkdtemp(template_hi));
+
+        name = strappenda(template_lo, "/script");
+        name2 = strappenda(template_hi, "/script2");
+        name3 = strappenda(template_lo, "/useless");
+        overridden = strappenda(template_lo, "/overridden");
+        override = strappenda(template_hi, "/overridden");
+        masked = strappenda(template_lo, "/masked");
+        mask = strappenda(template_hi, "/masked");
 
         assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works") == 0);
         assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2") == 0);
+        assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed") == 0);
+        assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0") == 0);
+        assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed") == 0);
+        assert_se(symlink("/dev/null", mask) == 0);
         assert_se(chmod(name, 0755) == 0);
         assert_se(chmod(name2, 0755) == 0);
+        assert_se(chmod(overridden, 0755) == 0);
+        assert_se(chmod(override, 0755) == 0);
+        assert_se(chmod(masked, 0755) == 0);
         assert_se(touch(name3) >= 0);
 
-        assert(chdir(template) == 0);
         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL);
+
+        assert(chdir(template_lo) == 0);
         assert_se(access("it_works", F_OK) >= 0);
+        assert_se(access("failed", F_OK) < 0);
+
+        assert(chdir(template_hi) == 0);
         assert_se(access("it_works2", F_OK) >= 0);
+        assert_se(access("failed", F_OK) < 0);
 
-        rm_rf_dangerous(template, false, true, false);
+        rm_rf_dangerous(template_lo, false, true, false);
+        rm_rf_dangerous(template_hi, false, true, false);
 }
 
 static void test_unquote_first_word(void) {

commit e801700e9acdde60078eb1d41b41b06369b83541
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Jan 8 20:47:25 2015 -0500

    Implement masking and overriding of generators
    
    Sometimes it is necessary to stop a generator from running. Either
    because of a bug, or for testing, or some other reason. The only way
    to do that would be to rename or chmod the generator binary, which is
    inconvenient and does not survive upgrades. Allow masking and
    overriding generators similarly to units and other configuration
    files.
    
    For the systemd instance, masking would be more common, rather than
    overriding generators. For the user instances, it may also be useful
    for users to have generators in $XDG_CONFIG_HOME to augment or
    override system-wide generators.
    
    Directories are searched according to the usual scheme (/usr/lib,
    /usr/local/lib, /run, /etc), and files with the same name in higher
    priority directories override files with the same name in lower
    priority directories. Empty files and links to /dev/null mask a given
    name.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=87230

diff --git a/src/core/manager.c b/src/core/manager.c
index 4918091..e53f222 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -93,6 +93,8 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint
 static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
 static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
+static int manager_run_generators(Manager *m);
+static void manager_undo_generators(Manager *m);
 
 static int manager_watch_jobs_in_progress(Manager *m) {
         usec_t next;
@@ -1086,8 +1088,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         assert(m);
 
         dual_timestamp_get(&m->generators_start_timestamp);
-        manager_run_generators(m);
+        r = manager_run_generators(m);
         dual_timestamp_get(&m->generators_finish_timestamp);
+        if (r < 0)
+                return r;
 
         r = lookup_paths_init(
                         &m->lookup_paths, m->running_as, true,
@@ -2517,7 +2521,9 @@ int manager_reload(Manager *m) {
         lookup_paths_free(&m->lookup_paths);
 
         /* Find new unit paths */
-        manager_run_generators(m);
+        q = manager_run_generators(m);
+        if (q < 0 && r >= 0)
+                r = q;
 
         q = lookup_paths_init(
                         &m->lookup_paths, m->running_as, true,
@@ -2525,19 +2531,19 @@ int manager_reload(Manager *m) {
                         m->generator_unit_path,
                         m->generator_unit_path_early,
                         m->generator_unit_path_late);
-        if (q < 0)
+        if (q < 0 && r >= 0)
                 r = q;
 
         manager_build_unit_path_cache(m);
 
         /* First, enumerate what we can from all config files */
         q = manager_enumerate(m);
-        if (q < 0)
+        if (q < 0 && r >= 0)
                 r = q;
 
         /* Second, deserialize our stored data */
         q = manager_deserialize(m, f, fds);
-        if (q < 0)
+        if (q < 0 && r >= 0)
                 r = q;
 
         fclose(f);
@@ -2545,12 +2551,12 @@ int manager_reload(Manager *m) {
 
         /* Re-register notify_fd as event source */
         q = manager_setup_notify(m);
-        if (q < 0)
+        if (q < 0 && r >= 0)
                 r = q;
 
         /* Third, fire things up! */
         q = manager_coldplug(m);
-        if (q < 0)
+        if (q < 0 && r >= 0)
                 r = q;
 
         assert(m->n_reloading > 0);
@@ -2775,27 +2781,33 @@ static void trim_generator_dir(Manager *m, char **generator) {
         return;
 }
 
-void manager_run_generators(Manager *m) {
-        const char *generator_path;
+static int manager_run_generators(Manager *m) {
+        _cleanup_free_ char **paths = NULL;
         const char *argv[5];
+        char **path;
         int r;
 
         assert(m);
 
         if (m->test_run)
-                return;
+                return 0;
 
-        generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
+        paths = generator_paths(m->running_as);
+        if (!paths)
+                return log_oom();
 
         /* Optimize by skipping the whole process by not creating output directories
          * if no generators are found. */
-        if (access(generator_path, F_OK) != 0) {
+        STRV_FOREACH(path, paths) {
+                r = access(*path, F_OK);
+                if (r == 0)
+                        goto found;
                 if (errno != ENOENT)
-                        log_error_errno(errno, "Failed to open generator directory %s: %m",
-                                        generator_path);
-                return;
+                        log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
         }
+        return 0;
 
+ found:
         r = create_generator_dir(m, &m->generator_unit_path, "generator");
         if (r < 0)
                 goto finish;
@@ -2815,12 +2827,13 @@ void manager_run_generators(Manager *m) {
         argv[4] = NULL;
 
         RUN_WITH_UMASK(0022)
-                execute_directory(generator_path, DEFAULT_TIMEOUT_USEC, (char**) argv);
+                execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
 
 finish:
         trim_generator_dir(m, &m->generator_unit_path);
         trim_generator_dir(m, &m->generator_unit_path_early);
         trim_generator_dir(m, &m->generator_unit_path_late);
+        return r;
 }
 
 static void remove_generator_dir(Manager *m, char **generator) {
@@ -2837,7 +2850,7 @@ static void remove_generator_dir(Manager *m, char **generator) {
         *generator = NULL;
 }
 
-void manager_undo_generators(Manager *m) {
+static void manager_undo_generators(Manager *m) {
         assert(m);
 
         remove_generator_dir(m, &m->generator_unit_path);
diff --git a/src/core/manager.h b/src/core/manager.h
index ab75f90..19fb0a9 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -349,9 +349,6 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name);
 
 void manager_check_finished(Manager *m);
 
-void manager_run_generators(Manager *m);
-void manager_undo_generators(Manager *m);
-
 void manager_recheck_journal(Manager *m);
 
 void manager_set_show_status(Manager *m, ShowStatus mode);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 4cbdf12..71f001a 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -49,6 +49,7 @@
 #include "cgroup-util.h"
 #include "def.h"
 #include "switch-root.h"
+#include "strv.h"
 
 #define FINALIZE_ATTEMPTS 50
 
@@ -159,6 +160,7 @@ int main(int argc, char *argv[]) {
         char *arguments[3];
         unsigned retries;
         int cmd, r;
+        static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
 
         log_parse_environment();
         r = parse_argv(argc, argv);
@@ -308,7 +310,7 @@ int main(int argc, char *argv[]) {
         arguments[0] = NULL;
         arguments[1] = arg_verb;
         arguments[2] = NULL;
-        execute_directory(SYSTEM_SHUTDOWN_PATH, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
 
         if (!in_container && !in_initrd() &&
             access("/run/initramfs/shutdown", X_OK) == 0) {
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 051f1a4..1e5bb85 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -78,6 +78,33 @@ int user_runtime_dir(char **runtime_dir) {
         return 0;
 }
 
+static int user_data_home_dir(char **dir, const char *suffix) {
+        const char *e;
+        char *res;
+
+        /* We don't treat /etc/xdg/systemd here as the spec
+         * suggests because we assume that that is a link to
+         * /etc/systemd/ anyway. */
+
+        e = getenv("XDG_DATA_HOME");
+        if (e)
+                res = strappend(e, suffix);
+        else {
+                const char *home;
+
+                home = getenv("HOME");
+                if (home)
+                        res = strjoin(home, "/.local/share", suffix, NULL);
+                else
+                        return 0;
+        }
+        if (!res)
+                return -ENOMEM;
+
+        *dir = res;
+        return 0;
+}
+
 static char** user_dirs(
                 const char *generator,
                 const char *generator_early,
@@ -100,10 +127,12 @@ static char** user_dirs(
                 NULL
         };
 
-        const char *home, *e;
+        const char *e;
         _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
         _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
-        char **r = NULL;
+        _cleanup_free_ char **res = NULL;
+        char **tmp;
+        int r;
 
         /* Implement the mechanisms defined in
          *
@@ -115,33 +144,21 @@ static char** user_dirs(
          */
 
         if (user_config_home(&config_home) < 0)
-                goto fail;
+                return NULL;
 
         if (user_runtime_dir(&runtime_dir) < 0)
-                goto fail;
-
-        home = getenv("HOME");
+                return NULL;
 
         e = getenv("XDG_CONFIG_DIRS");
         if (e) {
                 config_dirs = strv_split(e, ":");
                 if (!config_dirs)
-                        goto fail;
+                        return NULL;
         }
 
-        /* We don't treat /etc/xdg/systemd here as the spec
-         * suggests because we assume that that is a link to
-         * /etc/systemd/ anyway. */
-
-        e = getenv("XDG_DATA_HOME");
-        if (e) {
-                if (asprintf(&data_home, "%s/systemd/user", e) < 0)
-                        goto fail;
-
-        } else if (home) {
-                if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
-                        goto fail;
-        }
+        r = user_data_home_dir(&data_home, "/systemd/user");
+        if (r < 0)
+                return NULL;
 
         e = getenv("XDG_DATA_DIRS");
         if (e)
@@ -151,58 +168,71 @@ static char** user_dirs(
                                      "/usr/share",
                                      NULL);
         if (!data_dirs)
-                goto fail;
+                return NULL;
 
         /* Now merge everything we found. */
         if (generator_early)
-                if (strv_extend(&r, generator_early) < 0)
-                        goto fail;
+                if (strv_extend(&res, generator_early) < 0)
+                        return NULL;
 
         if (config_home)
-                if (strv_extend(&r, config_home) < 0)
-                        goto fail;
+                if (strv_extend(&res, config_home) < 0)
+                        return NULL;
 
         if (!strv_isempty(config_dirs))
-                if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0)
-                        goto fail;
+                if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
+                        return NULL;
 
-        if (strv_extend_strv(&r, (char**) config_unit_paths) < 0)
-                goto fail;
+        if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
+                return NULL;
 
         if (runtime_dir)
-                if (strv_extend(&r, runtime_dir) < 0)
-                        goto fail;
+                if (strv_extend(&res, runtime_dir) < 0)
+                        return NULL;
 
-        if (strv_extend(&r, runtime_unit_path) < 0)
-                goto fail;
+        if (strv_extend(&res, runtime_unit_path) < 0)
+                return NULL;
 
         if (generator)
-                if (strv_extend(&r, generator) < 0)
-                        goto fail;
+                if (strv_extend(&res, generator) < 0)
+                        return NULL;
 
         if (data_home)
-                if (strv_extend(&r, data_home) < 0)
-                        goto fail;
+                if (strv_extend(&res, data_home) < 0)
+                        return NULL;
 
         if (!strv_isempty(data_dirs))
-                if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0)
-                        goto fail;
+                if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
+                        return NULL;
 
-        if (strv_extend_strv(&r, (char**) data_unit_paths) < 0)
-                goto fail;
+        if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
+                return NULL;
 
         if (generator_late)
-                if (strv_extend(&r, generator_late) < 0)
-                        goto fail;
+                if (strv_extend(&res, generator_late) < 0)
+                        return NULL;
 
-        if (!path_strv_make_absolute_cwd(r))
-                goto fail;
+        if (!path_strv_make_absolute_cwd(res))
+                return NULL;
 
-        return r;
+        tmp = res;
+        res = NULL;
+        return tmp;
+}
 
-fail:
-        strv_free(r);
-        return NULL;
+char **generator_paths(SystemdRunningAs running_as) {
+        if (running_as == SYSTEMD_USER)
+                return strv_new("/etc/systemd/user-generators",
+                                "/run/systemd/user-generators",
+                                "/usr/local/lib/systemd/user-generators",
+                                USER_GENERATOR_PATH,
+                                NULL);
+        else
+                return strv_new("/etc/systemd/system-generators",
+                                "/run/systemd/system-generators",
+                                "/usr/local/lib/systemd/system-generators",
+                                SYSTEM_GENERATOR_PATH,
+                                NULL);
 }
 
 int lookup_paths_init(
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 655e454..2ec888d 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -42,6 +42,8 @@ typedef enum SystemdRunningAs {
 int user_config_home(char **config_home);
 int user_runtime_dir(char **runtime_dir);
 
+char **generator_paths(SystemdRunningAs running_as);
+
 int lookup_paths_init(LookupPaths *p,
                       SystemdRunningAs running_as,
                       bool personal,
diff --git a/src/shared/util.c b/src/shared/util.c
index 06bd1b9..6520e51 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4037,10 +4037,10 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
         return endswith(de->d_name, suffix);
 }
 
-static int do_execute(const char *directory, usec_t timeout, char *argv[]) {
+static int do_execute(char **directories, usec_t timeout, char *argv[]) {
         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
-        _cleanup_closedir_ DIR *d;
-        struct dirent *de;
+        _cleanup_set_free_free_ Set *seen = NULL;
+        char **directory;
 
         /* We fork this all off from a child process so that we can
          * somewhat cleanly make use of SIGALRM to set a time limit */
@@ -4050,57 +4050,80 @@ static int do_execute(const char *directory, usec_t timeout, char *argv[]) {
 
         assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
 
-        d = opendir(directory);
-        if (!d) {
-                if (errno == ENOENT)
-                        return 0;
-
-                return log_error_errno(errno, "Failed to open directory %s: %m", directory);
-        }
-
         pids = hashmap_new(NULL);
         if (!pids)
                 return log_oom();
 
-        FOREACH_DIRENT(de, d, break) {
-                _cleanup_free_ char *path = NULL;
-                pid_t pid;
-                int r;
+        seen = set_new(&string_hash_ops);
+        if (!seen)
+                return log_oom();
 
-                if (!dirent_is_file(de))
-                        continue;
+        STRV_FOREACH(directory, directories) {
+                _cleanup_closedir_ DIR *d;
+                struct dirent *de;
 
-                path = strjoin(directory, "/", de->d_name, NULL);
-                if (!path)
-                        return log_oom();
+                d = opendir(*directory);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
 
-                pid = fork();
-                if (pid < 0) {
-                        log_error_errno(errno, "Failed to fork: %m");
-                        continue;
-                } else if (pid == 0) {
-                        char *_argv[2];
+                        return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
+                }
 
-                        assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+                FOREACH_DIRENT(de, d, break) {
+                        _cleanup_free_ char *path = NULL;
+                        pid_t pid;
+                        int r;
 
-                        if (!argv) {
-                                _argv[0] = path;
-                                _argv[1] = NULL;
-                                argv = _argv;
+                        if (!dirent_is_file(de))
+                                continue;
+
+                        if (set_contains(seen, de->d_name)) {
+                                log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
+                                continue;
+                        }
+
+                        r = set_put_strdup(seen, de->d_name);
+                        if (r < 0)
+                                return log_oom();
+
+                        path = strjoin(*directory, "/", de->d_name, NULL);
+                        if (!path)
+                                return log_oom();
+
+                        if (null_or_empty_path(path)) {
+                                log_debug("%s is empty (a mask).", path);
+                                continue;
                         } else
-                                argv[0] = path;
+                                log_debug("%s will be executed.", path);
 
-                        execv(path, argv);
-                        return log_error_errno(errno, "Failed to execute %s: %m", path);
-                }
+                        pid = fork();
+                        if (pid < 0) {
+                                log_error_errno(errno, "Failed to fork: %m");
+                                continue;
+                        } else if (pid == 0) {
+                                char *_argv[2];
 
-                log_debug("Spawned %s as " PID_FMT ".", path, pid);
+                                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
 
-                r = hashmap_put(pids, UINT_TO_PTR(pid), path);
-                if (r < 0)
-                        return log_oom();
+                                if (!argv) {
+                                        _argv[0] = path;
+                                        _argv[1] = NULL;
+                                        argv = _argv;
+                                } else
+                                        argv[0] = path;
+
+                                execv(path, argv);
+                                return log_error_errno(errno, "Failed to execute %s: %m", path);
+                        }
+
+                        log_debug("Spawned %s as " PID_FMT ".", path, pid);
 
-                path = NULL;
+                        r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+                        if (r < 0)
+                                return log_oom();
+                        path = NULL;
+                }
         }
 
         /* Abort execution of this process after the timout. We simply
@@ -4126,14 +4149,21 @@ static int do_execute(const char *directory, usec_t timeout, char *argv[]) {
         return 0;
 }
 
-void execute_directory(const char *directory, usec_t timeout, char *argv[]) {
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
         pid_t executor_pid;
         int r;
+        char *name;
+        char **dirs = (char**) directories;
+
+        assert(!strv_isempty(dirs));
 
-        assert(directory);
+        name = basename(dirs[0]);
+        assert(!isempty(name));
 
-        /* Executes all binaries in the directory in parallel and waits
-         * for them to finish. Optionally a timeout is applied. */
+        /* Executes all binaries in the directories in parallel and waits
+         * for them to finish. Optionally a timeout is applied. If a file
+         * with the same name exists in more than one directory, the
+         * earliest one wins. */
 
         executor_pid = fork();
         if (executor_pid < 0) {
@@ -4141,11 +4171,11 @@ void execute_directory(const char *directory, usec_t timeout, char *argv[]) {
                 return;
 
         } else if (executor_pid == 0) {
-                r = do_execute(directory, timeout, argv);
+                r = do_execute(dirs, timeout, argv);
                 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
         }
 
-        wait_for_terminate_and_warn(directory, executor_pid, true);
+        wait_for_terminate_and_warn(name, executor_pid, true);
 }
 
 int kill_and_sigcont(pid_t pid, int sig) {
diff --git a/src/shared/util.h b/src/shared/util.h
index 3d31cb3..141a3fe 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -534,7 +534,7 @@ bool tty_is_console(const char *tty) _pure_;
 int vtnr_from_tty(const char *tty);
 const char *default_term_for_tty(const char *tty);
 
-void execute_directory(const char *directory, usec_t timeout, char *argv[]);
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
 
 int kill_and_sigcont(pid_t pid, int sig);
 
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 2fb5f0c..cc1ffa6 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -92,6 +92,7 @@ static int execute(char **modes, char **states) {
                 arg_verb,
                 NULL
         };
+        static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL};
 
         int r;
         _cleanup_fclose_ FILE *f = NULL;
@@ -107,7 +108,7 @@ static int execute(char **modes, char **states) {
         if (r < 0)
                 return r;
 
-        execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
 
         log_struct(LOG_INFO,
                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
@@ -126,7 +127,7 @@ static int execute(char **modes, char **states) {
                    NULL);
 
         arguments[1] = (char*) "post";
-        execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
 
         return r;
 }
diff --git a/src/test/test-util.c b/src/test/test-util.c
index d862149..1a59978 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1207,23 +1207,28 @@ static void test_glob_exists(void) {
 }
 
 static void test_execute_directory(void) {
-        char name[] = "/tmp/test-execute_directory/script1";
-        char name2[] = "/tmp/test-execute_directory/script2";
-        char name3[] = "/tmp/test-execute_directory/useless";
-        char tempdir[] = "/tmp/test-execute_directory/";
+        char template[] = "/tmp/test-readlink_and_make_absolute.XXXXXXX";
+        const char const* dirs[] = {template, NULL};
+        const char *name, *name2, *name3;
 
-        assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid()) >= 0);
-        assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch /tmp/test-execute_directory/it_works") == 0);
-        assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch /tmp/test-execute_directory/it_works2") == 0);
+        assert_se(mkdtemp(template));
+
+        name = strappenda(template, "/script");
+        name2 = strappenda(template, "/script2");
+        name3 = strappenda(template, "/useless");
+
+        assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works") == 0);
+        assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2") == 0);
         assert_se(chmod(name, 0755) == 0);
         assert_se(chmod(name2, 0755) == 0);
         assert_se(touch(name3) >= 0);
 
-        execute_directory(tempdir, DEFAULT_TIMEOUT_USEC, NULL);
-        assert_se(access("/tmp/test-execute_directory/it_works", F_OK) >= 0);
-        assert_se(access("/tmp/test-execute_directory/it_works2", F_OK) >= 0);
+        assert(chdir(template) == 0);
+        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL);
+        assert_se(access("it_works", F_OK) >= 0);
+        assert_se(access("it_works2", F_OK) >= 0);
 
-        rm_rf_dangerous(tempdir, false, true, false);
+        rm_rf_dangerous(template, false, true, false);
 }
 
 static void test_unquote_first_word(void) {

commit 4968105790c65af58d4ab42bffa2a4bedc0be8ee
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Jan 8 17:30:07 2015 -0500

    Simplify execute_directory()
    
    Remove the optional sepearate opening of the directory,
    it would be just too complicated with the change to
    multiple directories.
    
    Move the middle of execute_directory() to a seperate
    function to make it easier to grok.

diff --git a/src/core/manager.c b/src/core/manager.c
index c18312a..4918091 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2776,7 +2776,6 @@ static void trim_generator_dir(Manager *m, char **generator) {
 }
 
 void manager_run_generators(Manager *m) {
-        _cleanup_closedir_ DIR *d = NULL;
         const char *generator_path;
         const char *argv[5];
         int r;
@@ -2787,13 +2786,13 @@ void manager_run_generators(Manager *m) {
                 return;
 
         generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
-        d = opendir(generator_path);
-        if (!d) {
-                if (errno == ENOENT)
-                        return;
 
-                log_error_errno(errno, "Failed to enumerate generator directory %s: %m",
-                          generator_path);
+        /* Optimize by skipping the whole process by not creating output directories
+         * if no generators are found. */
+        if (access(generator_path, F_OK) != 0) {
+                if (errno != ENOENT)
+                        log_error_errno(errno, "Failed to open generator directory %s: %m",
+                                        generator_path);
                 return;
         }
 
@@ -2816,7 +2815,7 @@ void manager_run_generators(Manager *m) {
         argv[4] = NULL;
 
         RUN_WITH_UMASK(0022)
-                execute_directory(generator_path, d, DEFAULT_TIMEOUT_USEC, (char**) argv);
+                execute_directory(generator_path, DEFAULT_TIMEOUT_USEC, (char**) argv);
 
 finish:
         trim_generator_dir(m, &m->generator_unit_path);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 6492b19..4cbdf12 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -308,7 +308,7 @@ int main(int argc, char *argv[]) {
         arguments[0] = NULL;
         arguments[1] = arg_verb;
         arguments[2] = NULL;
-        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directory(SYSTEM_SHUTDOWN_PATH, DEFAULT_TIMEOUT_USEC, arguments);
 
         if (!in_container && !in_initrd() &&
             access("/run/initramfs/shutdown", X_OK) == 0) {
diff --git a/src/shared/util.c b/src/shared/util.c
index 7d753e4..06bd1b9 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4037,117 +4037,112 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
         return endswith(de->d_name, suffix);
 }
 
-void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv[]) {
-        pid_t executor_pid;
-        int r;
+static int do_execute(const char *directory, usec_t timeout, char *argv[]) {
+        _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+        _cleanup_closedir_ DIR *d;
+        struct dirent *de;
 
-        assert(directory);
+        /* We fork this all off from a child process so that we can
+         * somewhat cleanly make use of SIGALRM to set a time limit */
 
-        /* Executes all binaries in a directory in parallel and waits
-         * for them to finish. Optionally a timeout is applied. */
+        reset_all_signal_handlers();
+        reset_signal_mask();
 
-        executor_pid = fork();
-        if (executor_pid < 0) {
-                log_error_errno(errno, "Failed to fork: %m");
-                return;
+        assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
 
-        } else if (executor_pid == 0) {
-                _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
-                _cleanup_closedir_ DIR *_d = NULL;
-                struct dirent *de;
+        d = opendir(directory);
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
 
-                /* We fork this all off from a child process so that
-                 * we can somewhat cleanly make use of SIGALRM to set
-                 * a time limit */
+                return log_error_errno(errno, "Failed to open directory %s: %m", directory);
+        }
 
-                reset_all_signal_handlers();
-                reset_signal_mask();
+        pids = hashmap_new(NULL);
+        if (!pids)
+                return log_oom();
 
-                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+        FOREACH_DIRENT(de, d, break) {
+                _cleanup_free_ char *path = NULL;
+                pid_t pid;
+                int r;
 
-                if (!d) {
-                        d = _d = opendir(directory);
-                        if (!d) {
-                                if (errno == ENOENT)
-                                        _exit(EXIT_SUCCESS);
+                if (!dirent_is_file(de))
+                        continue;
 
-                                log_error_errno(errno, "Failed to enumerate directory %s: %m", directory);
-                                _exit(EXIT_FAILURE);
-                        }
-                }
+                path = strjoin(directory, "/", de->d_name, NULL);
+                if (!path)
+                        return log_oom();
 
-                pids = hashmap_new(NULL);
-                if (!pids) {
-                        log_oom();
-                        _exit(EXIT_FAILURE);
-                }
+                pid = fork();
+                if (pid < 0) {
+                        log_error_errno(errno, "Failed to fork: %m");
+                        continue;
+                } else if (pid == 0) {
+                        char *_argv[2];
 
-                FOREACH_DIRENT(de, d, break) {
-                        _cleanup_free_ char *path = NULL;
-                        pid_t pid;
+                        assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
 
-                        if (!dirent_is_file(de))
-                                continue;
+                        if (!argv) {
+                                _argv[0] = path;
+                                _argv[1] = NULL;
+                                argv = _argv;
+                        } else
+                                argv[0] = path;
 
-                        path = strjoin(directory, "/", de->d_name, NULL);
-                        if (!path) {
-                                log_oom();
-                                _exit(EXIT_FAILURE);
-                        }
+                        execv(path, argv);
+                        return log_error_errno(errno, "Failed to execute %s: %m", path);
+                }
 
-                        pid = fork();
-                        if (pid < 0) {
-                                log_error_errno(errno, "Failed to fork: %m");
-                                continue;
-                        } else if (pid == 0) {
-                                char *_argv[2];
+                log_debug("Spawned %s as " PID_FMT ".", path, pid);
 
-                                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+                r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+                if (r < 0)
+                        return log_oom();
 
-                                if (!argv) {
-                                        _argv[0] = path;
-                                        _argv[1] = NULL;
-                                        argv = _argv;
-                                } else
-                                        argv[0] = path;
+                path = NULL;
+        }
 
-                                execv(path, argv);
-                                log_error_errno(errno, "Failed to execute %s: %m", path);
-                                _exit(EXIT_FAILURE);
-                        }
+        /* Abort execution of this process after the timout. We simply
+         * rely on SIGALRM as default action terminating the process,
+         * and turn on alarm(). */
 
-                        log_debug("Spawned %s as " PID_FMT ".", path, pid);
+        if (timeout != USEC_INFINITY)
+                alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
 
-                        r = hashmap_put(pids, UINT_TO_PTR(pid), path);
-                        if (r < 0) {
-                                log_oom();
-                                _exit(EXIT_FAILURE);
-                        }
+        while (!hashmap_isempty(pids)) {
+                _cleanup_free_ char *path = NULL;
+                pid_t pid;
 
-                        path = NULL;
-                }
+                pid = PTR_TO_UINT(hashmap_first_key(pids));
+                assert(pid > 0);
+
+                path = hashmap_remove(pids, UINT_TO_PTR(pid));
+                assert(path);
 
-                /* Abort execution of this process after the
-                 * timout. We simply rely on SIGALRM as default action
-                 * terminating the process, and turn on alarm(). */
+                wait_for_terminate_and_warn(path, pid, true);
+        }
 
-                if (timeout != USEC_INFINITY)
-                        alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+        return 0;
+}
 
-                while (!hashmap_isempty(pids)) {
-                        _cleanup_free_ char *path = NULL;
-                        pid_t pid;
+void execute_directory(const char *directory, usec_t timeout, char *argv[]) {
+        pid_t executor_pid;
+        int r;
 
-                        pid = PTR_TO_UINT(hashmap_first_key(pids));
-                        assert(pid > 0);
+        assert(directory);
 
-                        path = hashmap_remove(pids, UINT_TO_PTR(pid));
-                        assert(path);
+        /* Executes all binaries in the directory in parallel and waits
+         * for them to finish. Optionally a timeout is applied. */
 
-                        wait_for_terminate_and_warn(path, pid, true);
-                }
+        executor_pid = fork();
+        if (executor_pid < 0) {
+                log_error_errno(errno, "Failed to fork: %m");
+                return;
 
-                _exit(EXIT_SUCCESS);
+        } else if (executor_pid == 0) {
+                r = do_execute(directory, timeout, argv);
+                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
         }
 
         wait_for_terminate_and_warn(directory, executor_pid, true);
diff --git a/src/shared/util.h b/src/shared/util.h
index 3b3035a..3d31cb3 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -534,7 +534,7 @@ bool tty_is_console(const char *tty) _pure_;
 int vtnr_from_tty(const char *tty);
 const char *default_term_for_tty(const char *tty);
 
-void execute_directory(const char *directory, DIR *_d, usec_t timeout, char *argv[]);
+void execute_directory(const char *directory, usec_t timeout, char *argv[]);
 
 int kill_and_sigcont(pid_t pid, int sig);
 
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 56b963a..2fb5f0c 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -107,7 +107,7 @@ static int execute(char **modes, char **states) {
         if (r < 0)
                 return r;
 
-        execute_directory(SYSTEM_SLEEP_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments);
 
         log_struct(LOG_INFO,
                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
@@ -126,7 +126,7 @@ static int execute(char **modes, char **states) {
                    NULL);
 
         arguments[1] = (char*) "post";
-        execute_directory(SYSTEM_SLEEP_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
+        execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments);
 
         return r;
 }
diff --git a/src/test/test-util.c b/src/test/test-util.c
index d529a21..d862149 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1219,7 +1219,7 @@ static void test_execute_directory(void) {
         assert_se(chmod(name2, 0755) == 0);
         assert_se(touch(name3) >= 0);
 
-        execute_directory(tempdir, NULL, DEFAULT_TIMEOUT_USEC, NULL);
+        execute_directory(tempdir, DEFAULT_TIMEOUT_USEC, NULL);
         assert_se(access("/tmp/test-execute_directory/it_works", F_OK) >= 0);
         assert_se(access("/tmp/test-execute_directory/it_works2", F_OK) >= 0);
 



More information about the systemd-commits mailing list