diff options
32 files changed, 2589 insertions, 1578 deletions
@@ -63,3 +63,4 @@ Arnd Bergmann <arnd@arndb.de> Tom Rini <trini@kernel.crashing.org> Paul Mundt <lethal@linux-sh.org> Atul Sabharwal <atul.sabharwal@intel.com> +Daniel Machon <Danielmachon@live.dk> diff --git a/Makefile.am b/Makefile.am index 3e5d7b49b1..9b93d0025c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1493,7 +1493,8 @@ tests += \ test-verbs \ test-af-list \ test-arphrd-list \ - test-dns-domain + test-dns-domain \ + test-install-root EXTRA_DIST += \ test/a.service \ @@ -1836,6 +1837,12 @@ test_verbs_SOURCES = \ test_verbs_LDADD = \ libshared.la +test_install_root_SOURCES = \ + src/test/test-install-root.c + +test_install_root_LDADD = \ + libshared.la + test_namespace_LDADD = \ libcore.la @@ -2,10 +2,6 @@ systemd System and Service Manager CHANGES WITH 228: - * When creating transient services via the bus API it is now - possible to pass in a set of file descriptors to use as - STDIN/STDOUT/STDERR for the invoked process. - * A number of properties previously only settable in unit files are now also available as properties to set when creating transient units programmatically via the bus, as it @@ -16,29 +12,9 @@ CHANGES WITH 228: ReadOnlyDirectories=, InaccessibleDirectories=, ProtectSystem=, ProtectHome=, RuntimeDirectory=. - * Any reference to /etc/mtab has been dropped from - systemd. The file has been obsolete since a while, but - systemd refused to work on systems where it was incorrectly - set up (it should be a symlink or non-existant). Please make - sure to update to util-linux 2.27.1 or newer in conjunction - with this systemd release, which also drops any reference to - /etc/mtab. If you maintain a distribution make sure that no - software you package still references it, as this is a - likely source of bugs. There's also a glibc bug pending, - asking for removal of any reference to the file: - - https://sourceware.org/bugzilla/show_bug.cgi?id=19108 - - * The compression framing format used by the journal or - coredump processing has changed to be in line with what the - official LZ4 tools generate. LZ4 compression support in - systemd was considered unsupported previously, as the format - was not compatible with the normal tools. With this release - this has changed now, and it is hence safe for downstream - distributions to turn it on. While not compressing as well - as the XZ,LZ4 is substantially faster, which makes - it a good default choice for the compression logic in the - journal and in coredump handling. + * When creating transient services via the bus API it is now + possible to pass in a set of file descriptors to use as + STDIN/STDOUT/STDERR for the invoked process. * Wherever systemd expects a calendar timestamp specification (like in journalctl's --since= and --until= switches) UTC @@ -49,6 +25,10 @@ CHANGES WITH 228: these additions also apply to recurring calendar event specification, such as OnCalendar= in timer units. + * journalctl gained a new "--sync" switch that asks the + journal daemon to write all so far unwritten log messages to + disk and sync the files, before returning. + * systemd-tmpfiles learned two new line types "q" and "Q" that operate like "v", but also set up a basic btrfs quota hierarchy when used on a btrfs file system with quota @@ -57,35 +37,96 @@ CHANGES WITH 228: * systemd-detect-virt gained a new --chroot switch to detect whether execution takes place in a chroot() environment. - * CPUAffinity= now takes CPU index ranges in addition to just + * CPUAffinity= now takes CPU index ranges in addition to individual indexes. + * The various memory-related resource limit settings (such as + LimitAS=) now understand the usual K, M, G, ... suffixes to + the base of 1024 (IEC). Similar, the time-related resource + limit settings understand the usual min, h, day, ... + suffixes now. + + * systemd-nspawn gained the new --network-veth-extra= switch + to define additional and arbitrarily-named virtual Ethernet + links between the host and the container. + + * A new service execution setting PassEnvironment= has been + added that allows importing select environment variables + from PID1's environment block into the environment block of + the service. + * systemd will now bump the net.unix.max_dgram_qlen to 512 by - default now (kernel default is 16). This is beneficial for - avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it + default now (the kernel default is 16). This is beneficial + for avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it allows substantially larger numbers of queued datagrams. This should increase the capability of systemd to parallelize boot-up, as logging and sd_notify() are unlikely to stall execution anymore. If you need to change the value from the new defaults, use the usual sysctl.d/ snippets. - * The various memory-related resource limit settings (such as - LimitAS=) now understand the usual K, M, G, ... suffixes to - the base of 1024 (IEC). Similar, the time-related settings - understand the usual min, h, day, ... suffixes now. + * The compression framing format used by the journal or + coredump processing has changed to be in line with what the + official LZ4 tools generate. LZ4 compression support in + systemd was considered unsupported previously, as the format + was not compatible with the normal tools. With this release + this has changed now, and it is hence safe for downstream + distributions to turn it on. While not compressing as well + as the XZ,LZ4 is substantially faster, which makes + it a good default choice for the compression logic in the + journal and in coredump handling. - * journalctl gained a new "--sync" switch that asks the - journal daemon to write all so far unwritten log messages to - disk and sync the files, before returning. + * Any reference to /etc/mtab has been dropped from + systemd. The file has been obsolete since a while, but + systemd refused to work on systems where it was incorrectly + set up (it should be a symlink or non-existant). Please make + sure to update to util-linux 2.27.1 or newer in conjunction + with this systemd release, which also drops any reference to + /etc/mtab. If you maintain a distribution make sure that no + software you package still references it, as this is a + likely source of bugs. There's also a glibc bug pending, + asking for removal of any reference to this obsolete file: + + https://sourceware.org/bugzilla/show_bug.cgi?id=19108 * Support for the ".snapshot" unit type has been removed. This feature turned out to be little useful and little used, and has now been removed from the core and from systemctl. - * A new service execution setting PassEnvironment= has been - added that allows importing select environment variables - from PID1's environment block into the environment block of - the service. + * In unit files the behaviour of %u, %U, %h, %s has + changed. These specifiers will now unconditionally resolve + to the various user database fields of the user that the + systemd instance is running as, instead of the user + configured in the specific unit via User=. Note that this + effectively doesn't change much, as resolving of these + specifiers was already turned off in the --system instance + of systemd, as we cannot do NSS lookups from PID 1. In the + --user instance of systemd these specifiers where correctly + resolved, but hardly made any sense, since the user instance + lacks privileges to do user switches anyway, and User= is + hence useless. Morever, even in the --user instance of + systemd behaviour was awkward as it would only take settings + from User= assignment placed before the specifier into + account. In order to unify and simplify the logic around + this the specifiers will now always resolve to the + credentials of the user invoking the manager (which in case + of PID 1 is the root user). + + Contributions from: Andrew Jones, Beniamino Galvani, Boyuan + Yang, Daniel Machon, Daniel Mack, David Herrmann, David + Reynolds, David Strauss, Dongsu Park, Evgeny Vereshchagin, + Filipe Brandenburger, Franck Bui, Hristo Venev, Iago López + Galeiras, Jan Engelhardt, Jan Janssen, Jan Synacek, Jesus + Ornelas Aguayo, Karel Zak, kayrus, Kay Sievers, Lennart + Poettering, Mantas Mikulėnas, Marcel Holtmann, Marcin Bachry, + Marcos Alano, Marcos Mello, Mark Theunissen, Martin Pitt, + Michael Marineau, Michael Olbrich, Michal Schmidt, Michal + Sekletar, Mirco Tischler, Nick Owens, Nicolas Cornu, Patrik + Flykt, Peter Hutterer, reverendhomer, Ronny Chevalier, + Sangjung Woo, Seong-ho Cho, Shawn Landden, Susant Sahani, + Thomas Haller, Thomas Hindoe Paaboel Andersen, Tom Gundersen, + Torstein Husebø, Vito Caputo, Zbigniew Jędrzejewski-Szmek + + -- Berlin, 2015-11-XX CHANGES WITH 227: @@ -76,9 +76,6 @@ Features: * nspawn: fix logic always print a final newline on output. https://github.com/systemd/systemd/pull/272#issuecomment-113153176 -* make nspawn's --network-veth switch more powerful: - http://lists.freedesktop.org/archives/systemd-devel/2015-June/033121.html - * man: document that unless you use StandardError=null the shell >/dev/stderr won't work in shell scripts in services * "systemctl daemon-reload" should result in /etc/systemd/system.conf being reloaded by systemd diff --git a/systemd-master/catalog/systemd.da.catalog b/catalog/systemd.da.catalog index dc3f8b552e..bd4d742d8a 100644 --- a/systemd-master/catalog/systemd.da.catalog +++ b/catalog/systemd.da.catalog @@ -1,261 +1,261 @@ -# This file is part of systemd.
-#
-# Copyright 2012 Lennart Poettering
-#
-# 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/>.
-
-# Message catalog for systemd's own messages
-# Danish translation
-
-# The catalog format is documented on
-# http://www.freedesktop.org/wiki/Software/systemd/catalog
-
-# For an explanation why we do all this, see https://xkcd.com/1024/
-
--- f77379a8490b408bbe5f6940505a777b
-Subject: Journalen er blevet startet
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-System-journal processen har startet op, åbnet journal filerne for
-tilskrivning og er nu klar til at modtage anmodninger.
-
--- d93fb3c9c24d451a97cea615ce59c00b
-Subject: Journalen er blevet stoppet
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-System-journal processen er stoppet og har lukket alle aktive journal
-filer.
-
--- a596d6fe7bfa4994828e72309e95d61e
-Subject: Beskeder fra en service er blevet undertrykt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: man:journald.conf(5)
-
-En service har logget for mange beskeder inden for en given tidsperiode.
-Beskeder fra omtalte service er blevet smidt væk.
-
-Kun beskeder fra omtalte service er smidt væk. Beskeder fra andre
-services er ikke påvirket.
-
-Grænsen for hvornår beskeder bliver smidt væk kan konfigureres
-med RateLimitInterval= og RateLimitBurst= i
-/etc/systemd/journald.conf. Se journald.conf(5) for detaljer herom.
-
--- e9bf28e6e834481bb6f48f548ad13606
-Subject: Journal beskeder er gået tabt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Kernel beskeder er gået tabt da journal systemet ikke har været i stand
-til at håndtere dem hurtigt nok.
-
--- fc2e22bc6ee647b6b90729ab34a250b1
-Subject: Fejl-fil genereret for process @COREDUMP_PID@ (@COREDUMP_COMM@)
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: man:core(5)
-
-Process @COREDUMP_PID@ (@COREDUMP_COMM@) har lukket ned og genereret en
-fejl-fil.
-
-Dette indikerer som regel en programmeringsfejl i det nedlukkede program
-og burde blive reporteret som en bug til folkene bag
-
--- 8d45620c1a4348dbb17410da57c60c66
-Subject: En ny session @SESSION_ID@ er blevet lavet for bruger @USER_ID@
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
-
-En ny session med ID @SESSION_ID@ er blevet lavet for brugeren @USER_ID@.
-
-Den ledende process for sessionen er @LEADER@.
-
--- 3354939424b4456d9802ca8333ed424a
-Subject: Session @SESSION_ID@ er blevet lukket ned
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
-
-En session med ID @SESSION_ID@ er blevet lukket ned.
-
--- fcbefc5da23d428093f97c82a9290f7b
-Subject: En ny arbejdsstation $SEAT_ID@ er nu tilgængelig
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
-
-En ny arbejdsstation @SEAT_ID@ er blevet konfigureret og er nu tilgængelig.
-
--- e7852bfe46784ed0accde04bc864c2d5
-Subject: Arbejdsstation @SEAT_ID@ er nu blevet fjernet
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
-
-En arbejdsstation @SEAT_ID@ er blevet fjernet og er ikke længere tilgængelig.
-
--- c7a787079b354eaaa9e77b371893cd27
-Subject: Tidsændring
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Systemtiden er blevet ændret til @REALTIME@ mikrosekunder efter d. 1. Januar 1970.
-
--- 45f82f4aef7a4bbf942ce861d1f20990
-Subject: Tidszoneændring til @TIMEZONE@
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Tidszonen for systemet er blevet ændret til @TIMEZONE@.
-
--- b07a249cd024414a82dd00cd181378ff
-Subject: Opstart af systemet er nu fuldført
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Alle system services i kø til at køre ved opstart, er blevet startet
-med success. Bemærk at dette ikke betyder at maskinen er i dvale, da
-services stadig kan være i gang med at færdiggøre deres opstart.
-
-Opstart af kernel tog @KERNEL_USEC@ mikrosekunder.
-
-Opstart af initrd tog @INITRD_USEC@ mikrosekunder.
-
-Opstart af userspace tog @USERSPACE_USEC@ mikrosekunder.
-
--- 6bbd95ee977941e497c48be27c254128
-Subject: System slumretilstand @SLEEP@ trådt i kraft
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-System er nu gået i @SLEEP@ slumretilstand.
-
--- 8811e6df2a8e40f58a94cea26f8ebf14
-Subject: System slumretilstand @SLEEP@ forladt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Systemet har nu forladt @SLEEP@ slumretilstand.
-
--- 98268866d1d54a499c4e98921d93bc40
-Subject: Systemnedlukning påbegyndt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Systemnedlukning er blevet påbegyndt. Nedlukningen er nu begyndt og
-alle system services er blevet afbrudt og alle filsystemer afmonteret.
-
--- 7d4958e842da4a758f6c1cdc7b36dcc5
-Subject: Enhed @UNIT@ har påbegyndt opstart
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ er begyndt at starte op.
-
--- 39f53479d3a045ac8e11786248231fbf
-Subject: Enhed @UNIT har færdiggjort opstart
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ er færdig med at starte op.
-
-Resultat for opstart er @RESULT@.
-
--- de5b426a63be47a7b6ac3eaac82e2f6f
-Subject: Enhed @UNIT@ har påbegyndt nedlukning
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ har påbegyndt nedlukning.
-
--- 9d1aaa27d60140bd96365438aad20286
-Subject: Enhed @UNIT@ har færdiggjort nedlukning
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ har færdiggjort nedlukning.
-
--- be02cf6855d2428ba40df7e9d022f03d
-Subject: Enhed @UNIT@ har fejlet
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ har fejlet.
-
-Resultatet er @RESULT@
-
--- d34d037fff1847e6ae669a370e694725
-Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ er begyndt at genindlæse sin konfiguration
-
--- 7b05ebc668384222baa8881179cfda54
-Subject: Enhed @UNIT@ har færdiggjort genindlæsning af sin konfiguration
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Enhed @UNIT@ er færdig med at genindlæse sin konfiguration
-
-Resultatet er: @RESULT@.
-
--- 641257651c1b4ec9a8624d7a40a9e1e7
-Subject: Process @EXECUTABLE@ kunne ikke eksekveres
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Processen @EXECUTABLE@ kunne ikke eksekveres og fejlede.
-
-Processens returnerede fejlkode er @ERRNO@.
-
--- 0027229ca0644181a76c4e92458afa2e
-Subject: Èn eller flere beskeder kunne ikke videresendes til syslog
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Èn eller flere beskeder kunne ikke videresendes til syslog servicen
-der kører side-om-side med journald. Dette indikerer typisk at syslog
-implementationen ikke har kunnet følge med mængden af ventende beskeder.
-
--- 1dee0369c7fc4736b7099b38ecb46ee7
-Subject: Monteringspunkt er ikke tomt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Folderen @WHERE@ er specificeret som monteringspunkt (andet felt i
-/etc/fstab eller Where= feltet i systemd enhedsfil) men er ikke tom.
-Dette forstyrrer ikke monteringen, men de pre-eksisterende filer i folderen
-bliver utilgængelige. For at se de over-monterede filer; montér det
-underlæggende filsystem til en anden lokation.
-
--- 24d8d4452573402496068381a6312df2
-Subject: En virtuel maskine eller container er blevet startet
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet
-startet og er klar til brug.
-
--- 58432bd3bace477cb514b56381b8a758
-Subject: En virtuel maskine eller container er blevet afbrudt
-Defined-By: systemd
-Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-
-Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet
-nedlukket.
+# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# 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/>. + +# Message catalog for systemd's own messages +# Danish translation + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Journalen er blevet startet +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +System-journal processen har startet op, åbnet journal filerne for +tilskrivning og er nu klar til at modtage anmodninger. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Journalen er blevet stoppet +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +System-journal processen er stoppet og har lukket alle aktive journal +filer. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Beskeder fra en service er blevet undertrykt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:journald.conf(5) + +En service har logget for mange beskeder inden for en given tidsperiode. +Beskeder fra omtalte service er blevet smidt væk. + +Kun beskeder fra omtalte service er smidt væk. Beskeder fra andre +services er ikke påvirket. + +Grænsen for hvornår beskeder bliver smidt væk kan konfigureres +med RateLimitInterval= og RateLimitBurst= i +/etc/systemd/journald.conf. Se journald.conf(5) for detaljer herom. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Journal beskeder er gået tabt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Kernel beskeder er gået tabt da journal systemet ikke har været i stand +til at håndtere dem hurtigt nok. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Fejl-fil genereret for process @COREDUMP_PID@ (@COREDUMP_COMM@) +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:core(5) + +Process @COREDUMP_PID@ (@COREDUMP_COMM@) har lukket ned og genereret en +fejl-fil. + +Dette indikerer som regel en programmeringsfejl i det nedlukkede program +og burde blive reporteret som en bug til folkene bag + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: En ny session @SESSION_ID@ er blevet lavet for bruger @USER_ID@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En ny session med ID @SESSION_ID@ er blevet lavet for brugeren @USER_ID@. + +Den ledende process for sessionen er @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Session @SESSION_ID@ er blevet lukket ned +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En session med ID @SESSION_ID@ er blevet lukket ned. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: En ny arbejdsstation $SEAT_ID@ er nu tilgængelig +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En ny arbejdsstation @SEAT_ID@ er blevet konfigureret og er nu tilgængelig. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Arbejdsstation @SEAT_ID@ er nu blevet fjernet +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En arbejdsstation @SEAT_ID@ er blevet fjernet og er ikke længere tilgængelig. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Tidsændring +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Systemtiden er blevet ændret til @REALTIME@ mikrosekunder efter d. 1. Januar 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Tidszoneændring til @TIMEZONE@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Tidszonen for systemet er blevet ændret til @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Opstart af systemet er nu fuldført +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Alle system services i kø til at køre ved opstart, er blevet startet +med success. Bemærk at dette ikke betyder at maskinen er i dvale, da +services stadig kan være i gang med at færdiggøre deres opstart. + +Opstart af kernel tog @KERNEL_USEC@ mikrosekunder. + +Opstart af initrd tog @INITRD_USEC@ mikrosekunder. + +Opstart af userspace tog @USERSPACE_USEC@ mikrosekunder. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: System slumretilstand @SLEEP@ trådt i kraft +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +System er nu gået i @SLEEP@ slumretilstand. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: System slumretilstand @SLEEP@ forladt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Systemet har nu forladt @SLEEP@ slumretilstand. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Systemnedlukning påbegyndt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Systemnedlukning er blevet påbegyndt. Nedlukningen er nu begyndt og +alle system services er blevet afbrudt og alle filsystemer afmonteret. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Enhed @UNIT@ har påbegyndt opstart +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ er begyndt at starte op. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Enhed @UNIT har færdiggjort opstart +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ er færdig med at starte op. + +Resultat for opstart er @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Enhed @UNIT@ har påbegyndt nedlukning +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ har påbegyndt nedlukning. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Enhed @UNIT@ har færdiggjort nedlukning +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ har færdiggjort nedlukning. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Enhed @UNIT@ har fejlet +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ har fejlet. + +Resultatet er @RESULT@ + +-- d34d037fff1847e6ae669a370e694725 +Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ er begyndt at genindlæse sin konfiguration + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Enhed @UNIT@ har færdiggjort genindlæsning af sin konfiguration +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Enhed @UNIT@ er færdig med at genindlæse sin konfiguration + +Resultatet er: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Process @EXECUTABLE@ kunne ikke eksekveres +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Processen @EXECUTABLE@ kunne ikke eksekveres og fejlede. + +Processens returnerede fejlkode er @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Èn eller flere beskeder kunne ikke videresendes til syslog +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Èn eller flere beskeder kunne ikke videresendes til syslog servicen +der kører side-om-side med journald. Dette indikerer typisk at syslog +implementationen ikke har kunnet følge med mængden af ventende beskeder. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Monteringspunkt er ikke tomt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Folderen @WHERE@ er specificeret som monteringspunkt (andet felt i +/etc/fstab eller Where= feltet i systemd enhedsfil) men er ikke tom. +Dette forstyrrer ikke monteringen, men de pre-eksisterende filer i folderen +bliver utilgængelige. For at se de over-monterede filer; montér det +underlæggende filsystem til en anden lokation. + +-- 24d8d4452573402496068381a6312df2 +Subject: En virtuel maskine eller container er blevet startet +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet +startet og er klar til brug. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: En virtuel maskine eller container er blevet afbrudt +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet +nedlukket. diff --git a/man/systemctl.xml b/man/systemctl.xml index 0e4eba9781..755a74f987 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -958,10 +958,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service <term><command>list-unit-files <optional><replaceable>PATTERN...</replaceable></optional></command></term> <listitem> - <para>List installed unit files. If one or more - <replaceable>PATTERN</replaceable>s are specified, only - units whose filename (just the last component of the path) - matches one of them are shown.</para> + <para>List installed unit files and their enablement state + (as reported by <command>is-enabled</command>). If one or + more <replaceable>PATTERN</replaceable>s are specified, + only units whose filename (just the last component of the + path) matches one of them are shown.</para> </listitem> </varlistentry> @@ -1131,7 +1132,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service <tbody> <row> <entry><literal>enabled</literal></entry> - <entry morerows='1'>Enabled through a symlink in <filename>.wants</filename> directory (permanently or just in <filename>/run</filename>).</entry> + <entry morerows='1'>Enabled through a symlink in a <filename>.wants/</filename> or <filename>.requires/</filename> subdirectory of <filename>/etc/systemd/system/</filename> (persistently) or <filename>/run/systemd/system/</filename> (transiently).</entry> <entry morerows='1'>0</entry> </row> <row> @@ -1139,7 +1140,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service </row> <row> <entry><literal>linked</literal></entry> - <entry morerows='1'>Made available through a symlink to the unit file (permanently or just in <filename>/run</filename>).</entry> + <entry morerows='1'>Made available through one or more symlinks to the unit file (permanently in <filename>/etc/systemd/system/</filename> or transiently in <filename>/run/systemd/system/</filename>), even though the unit file might reside outside of the unit file search path.</entry> <entry morerows='1'>> 0</entry> </row> <row> @@ -1147,7 +1148,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service </row> <row> <entry><literal>masked</literal></entry> - <entry morerows='1'>Disabled entirely (permanently or just in <filename>/run</filename>).</entry> + <entry morerows='1'>Completely disabled, so that any start operation on it fails (permanently in <filename>/etc/systemd/system/</filename> or transiently in <filename>/run/systemd/systemd/</filename>).</entry> <entry morerows='1'>> 0</entry> </row> <row> @@ -1165,7 +1166,12 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service </row> <row> <entry><literal>disabled</literal></entry> - <entry>The unit file is not enabled.</entry> + <entry>Unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry> + <entry>> 0</entry> + </row> + <row> + <entry><literal>bad</literal></entry> + <entry>Unit file is invalid or another error occured. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry> <entry>> 0</entry> </row> </tbody> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 4725604c03..a97b7c44eb 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -433,6 +433,21 @@ </varlistentry> <varlistentry> + <term><option>--network-veth-extra=</option></term> + + <listitem><para>Adds an additional virtual Ethernet link + between host and container. Takes a colon-separated pair of + host interface name and container interface name. The latter + may be omitted in which case the container and host sides will + be assigned the same name. This switch is independent of + <option>--network-veth</option>, and -- in contrast -- may be + used multiple times, and allows configuration of the network + interface names. Note that <option>--network-bridge=</option> + has no effect on interfaces created with + <option>--network-veth-extra=</option>.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>--network-bridge=</option></term> <listitem><para>Adds the host side of the Ethernet link diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index c2b7a40ec1..e952688331 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -324,6 +324,23 @@ </varlistentry> <varlistentry> + <term><varname>VirtualEthernetExtra=</varname></term> + + <listitem><para>Takes a colon-separated pair of interface + names. Configures an additional virtual Ethernet connection + (<literal>veth</literal>) between host and the container. The + first specified name is the interface name on the host, the + second the interface name in the container. The latter may be + omitted in which case it is set to the same name as the host + side interface. This setting implies + <varname>Private=yes</varname>. This setting corresponds to + the <option>--network-veth-extra=</option> command line + switch, and maybe be used multiple times. It is independent of + <varname>VirtualEthernet=</varname>. This option is privileged + (see above).</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>Interface=</varname></term> <listitem><para>Takes a space-separated list of interfaces to diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index c61469b2e7..5b12378eda 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1209,22 +1209,22 @@ <row> <entry><literal>%u</literal></entry> <entry>User name</entry> - <entry>This is the name of the configured user of the unit, or (if none is set) the user running the systemd instance.</entry> + <entry>This is the name of the user running the service manager instance. In case of the system manager this resolves to <literal>root</literal>.</entry> </row> <row> <entry><literal>%U</literal></entry> <entry>User UID</entry> - <entry>This is the numeric UID of the configured user of the unit, or (if none is set) the user running the systemd user instance. Note that this specifier is not available for units run by the systemd system instance (as opposed to those run by a systemd user instance), unless the user has been configured as a numeric UID in the first place or the configured user is the root user.</entry> + <entry>This is the numeric UID of the user running the service manager instance. In case of the system manager this resolves to <literal>0</literal>.</entry> </row> <row> <entry><literal>%h</literal></entry> <entry>User home directory</entry> - <entry>This is the home directory of the configured user of the unit, or (if none is set) the user running the systemd user instance. Similar to <literal>%U</literal>, this specifier is not available for units run by the systemd system instance, unless the configured user is the root user.</entry> + <entry>This is the home directory of the user running the service manager instance. In case of the system manager this resolves to <literal>/root</literal>.</entry> </row> <row> <entry><literal>%s</literal></entry> <entry>User shell</entry> - <entry>This is the shell of the configured user of the unit, or (if none is set) the user running the systemd user instance. Similar to <literal>%U</literal>, this specifier is not available for units run by the systemd system instance, unless the configured user is the root user.</entry> + <entry>This is the shell of the user running the service manager instance. In case of the system manager this resolves to <literal>/bin/sh</literal>.</entry> </row> <row> <entry><literal>%m</literal></entry> diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index cddd4232fc..2b6189ad90 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -233,6 +233,26 @@ int readlink_and_canonicalize(const char *p, char **r) { return 0; } +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) { + _cleanup_free_ char *target = NULL, *t = NULL; + const char *full; + int r; + + full = prefix_roota(root, path); + r = readlink_malloc(full, &target); + if (r < 0) + return r; + + t = file_in_same_dir(path, target); + if (!t) + return -ENOMEM; + + *ret = t; + t = NULL; + + return 0; +} + int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index b94873e65b..902c7e295b 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -40,6 +40,7 @@ int readlink_malloc(const char *p, char **r); int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); int readlink_and_canonicalize(const char *p, char **r); +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index eaa0fb2b37..d3bcc795ae 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1521,9 +1521,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - state = unit_file_get_state(scope, NULL, name); - if (state < 0) - return state; + r = unit_file_get_state(scope, NULL, name, &state); + if (r < 0) + return r; return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state)); } @@ -1651,6 +1651,8 @@ static int method_enable_unit_files_generic( scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; r = call(scope, runtime, NULL, l, force, &changes, &n_changes); + if (r == -ESHUTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); if (r < 0) return r; @@ -1885,6 +1887,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); + if (r == -ESHUTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); if (r < 0) return r; diff --git a/src/core/manager.c b/src/core/manager.c index a18868083f..f695b8a66c 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -486,7 +486,7 @@ static int manager_setup_signals(Manager *m) { * later than notify_fd processing, so that the notify * processing can still figure out to which process/service a * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, -5); + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return r; @@ -734,7 +734,7 @@ static int manager_setup_notify(Manager *m) { /* Process signals a bit earlier than SIGCHLD, so that we can * still identify to which service an exit message belongs */ - r = sd_event_source_set_priority(m->notify_event_source, -7); + r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 721c8ccce9..f587a5a141 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -159,162 +159,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char ** } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *printed = NULL; - Unit *u = userdata; - ExecContext *c; - int r = 0; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it in that case, and fail if we can't help - * it */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - printed = strdup(specifier == 'u' ? "root" : "0"); - else { - if (specifier == 'u') - printed = strdup(c->user); - else { - uid_t uid; + char *t; - r = parse_uid(c->user, &uid); - if (r < 0) - return -ENODATA; - - r = asprintf(&printed, UID_FMT, uid); - } - } - - } else { - _cleanup_free_ char *tmp = NULL; - const char *username = NULL; - uid_t uid; - - if (c->user) - username = c->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - /* fish username from passwd */ - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (specifier == 'u') - printed = strdup(username); - else - r = asprintf(&printed, UID_FMT, uid); - } + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ - if (r < 0 || !printed) + t = getusername_malloc(); + if (!t) return -ENOMEM; - *ret = printed; + *ret = t; return 0; } -static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/root"); - else - return -EOPNOTSUPP; - - } else { +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { - /* return HOME if set, otherwise from passwd */ - if (!c || !c->user) { - r = get_home_dir(&n); - if (r < 0) - return r; - } else { - const char *username, *home; - - username = c->user; - r = get_user_creds(&username, NULL, NULL, &home, NULL); - if (r < 0) - return r; - - n = strdup(home); - } - } - - if (!n) + if (asprintf(ret, UID_FMT, getuid()) < 0) return -ENOMEM; - *ret = n; return 0; } -static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/bin/sh"); - else - return -EOPNOTSUPP; - - } else { +static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - /* return /bin/sh for root, otherwise the value from passwd */ - if (!c->user) { - r = get_shell(&n); - if (r < 0) - return r; - } else { - const char *username, *shell; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - username = c->user; - r = get_user_creds(&username, NULL, NULL, NULL, &shell); - if (r < 0) - return r; + return get_home_dir(ret); +} - n = strdup(shell); - } - } +static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - if (!n) - return -ENOMEM; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - *ret = n; - return 0; + return get_shell(ret); } int unit_name_printf(Unit *u, const char* format, char **ret) { @@ -354,10 +235,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { * %r where units in this slice are placed in the cgroup tree * %R the root of this systemd's instance tree * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) - * %U the UID of the configured user or running user - * %u the username of the configured user or running user - * %h the homedir of the configured user or running user - * %s the shell of the configured user or running user + * %U the UID of the running user + * %u the username of the running user + * %h the homedir of the running user + * %s the shell of the running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -377,7 +258,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { { 'r', specifier_cgroup_slice, NULL }, { 'R', specifier_cgroup_root, NULL }, { 't', specifier_runtime, NULL }, - { 'U', specifier_user_name, NULL }, + + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'h', specifier_user_home, NULL }, { 's', specifier_user_shell, NULL }, diff --git a/src/core/unit.c b/src/core/unit.c index bcc6763c9d..f553f24829 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3132,12 +3132,19 @@ int unit_following_set(Unit *u, Set **s) { } UnitFileState unit_get_unit_file_state(Unit *u) { + int r; + assert(u); - if (u->unit_file_state < 0 && u->fragment_path) - u->unit_file_state = unit_file_get_state( + if (u->unit_file_state < 0 && u->fragment_path) { + r = unit_file_get_state( u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path), + &u->unit_file_state); + if (r < 0) + u->unit_file_state = UNIT_FILE_BAD; + } return u->unit_file_state; } @@ -3148,7 +3155,8 @@ int unit_get_unit_file_preset(Unit *u) { if (u->unit_file_preset < 0 && u->fragment_path) u->unit_file_preset = unit_file_query_preset( u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path)); return u->unit_file_preset; } diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index f35b3a33b8..0881973e7f 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -519,7 +519,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); - pref = ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF >> 3; + pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3; switch (pref) { case ND_RA_FLAG_PREF_LOW: diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index b5127a387c..58f9f4c635 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -15,24 +15,25 @@ struct ConfigPerfItem; %struct-type %includes %% -Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) -Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) -Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) -Exec.User, config_parse_string, 0, offsetof(Settings, user) -Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) -Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) -Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) -Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) -Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) -Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) -Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) -Files.Bind, config_parse_bind, 0, 0 -Files.BindReadOnly, config_parse_bind, 1, 0 -Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 -Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) -Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) -Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) -Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) -Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) -Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) -Network.Port, config_parse_expose_port, 0, 0 +Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) +Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) +Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) +Exec.User, config_parse_string, 0, offsetof(Settings, user) +Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) +Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) +Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) +Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) +Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) +Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) +Files.Bind, config_parse_bind, 0, 0 +Files.BindReadOnly, config_parse_bind, 1, 0 +Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) +Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) +Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) +Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) +Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) +Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 +Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 29384b60b2..c71552879d 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -37,6 +37,8 @@ #define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) #define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) +#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66) +#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59) #define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f) static int generate_mac( @@ -84,42 +86,32 @@ static int generate_mac( return 0; } -int setup_veth(const char *machine_name, - pid_t pid, - char iface_name[IFNAMSIZ], - bool bridge) { +static int add_veth( + sd_netlink *rtnl, + pid_t pid, + const char *ifname_host, + const struct ether_addr *mac_host, + const char *ifname_container, + const struct ether_addr *mac_container) { _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - struct ether_addr mac_host, mac_container; - int r, i; - - /* Use two different interface name prefixes depending whether - * we are in bridge mode or not. */ - snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", - bridge ? "vb" : "ve", machine_name); - - r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); - - r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); + int r; - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + assert(rtnl); + assert(ifname_host); + assert(mac_host); + assert(ifname_container); + assert(mac_container); r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); if (r < 0) return log_error_errno(r, "Failed to allocate netlink message: %m"); - r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name); + r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host); if (r < 0) return log_error_errno(r, "Failed to add netlink interface name: %m"); - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host); if (r < 0) return log_error_errno(r, "Failed to add netlink MAC address: %m"); @@ -135,11 +127,11 @@ int setup_veth(const char *machine_name, if (r < 0) return log_error_errno(r, "Failed to open netlink container: %m"); - r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0"); + r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container); if (r < 0) return log_error_errno(r, "Failed to add netlink interface name: %m"); - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container); if (r < 0) return log_error_errno(r, "Failed to add netlink MAC address: %m"); @@ -161,7 +153,44 @@ int setup_veth(const char *machine_name, r = sd_netlink_call(rtnl, m, 0, NULL); if (r < 0) - return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name); + return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container); + + return 0; +} + +int setup_veth(const char *machine_name, + pid_t pid, + char iface_name[IFNAMSIZ], + bool bridge) { + + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + struct ether_addr mac_host, mac_container; + int r, i; + + assert(machine_name); + assert(pid > 0); + assert(iface_name); + + /* Use two different interface name prefixes depending whether + * we are in bridge mode or not. */ + snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", + bridge ? "vb" : "ve", machine_name); + + r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); + + r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container); + if (r < 0) + return r; i = (int) if_nametoindex(iface_name); if (i <= 0) @@ -170,6 +199,47 @@ int setup_veth(const char *machine_name, return i; } +int setup_veth_extra( + const char *machine_name, + pid_t pid, + char **pairs) { + + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + uint64_t idx = 0; + char **a, **b; + int r; + + assert(machine_name); + assert(pid > 0); + + if (strv_isempty(pairs)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + STRV_FOREACH_PAIR(a, b, pairs) { + struct ether_addr mac_host, mac_container; + + r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m"); + + r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m"); + + r = add_veth(rtnl, pid, *a, &mac_host, *b, &mac_container); + if (r < 0) + return r; + + idx ++; + } + + return 0; +} + int setup_bridge(const char *veth_name, const char *bridge_name) { _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; @@ -439,3 +509,34 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { return 0; } + +int veth_extra_parse(char ***l, const char *p) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0 || isempty(a)) + return -EINVAL; + + r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0 || isempty(b)) { + free(b); + b = strdup(a); + if (!b) + return -ENOMEM; + } + + if (p) + return -EINVAL; + + r = strv_push_pair(l, a, b); + if (r < 0) + return -ENOMEM; + + a = b = NULL; + return 0; +} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 311e6d06cb..b86effef47 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -27,6 +27,7 @@ #include <stdbool.h> int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); +int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs); int setup_bridge(const char *veth_name, const char *bridge_name); @@ -34,3 +35,5 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces); int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); int move_network_interfaces(pid_t pid, char **ifaces); + +int veth_extra_parse(char ***l, const char *p); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 6885d0641e..d6b64d8d5a 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "cap-list.h" #include "conf-parser.h" +#include "nspawn-network.h" #include "nspawn-settings.h" #include "process-util.h" #include "strv.h" @@ -77,6 +78,7 @@ Settings* settings_free(Settings *s) { strv_free(s->network_interfaces); strv_free(s->network_macvlan); strv_free(s->network_ipvlan); + strv_free(s->network_veth_extra); free(s->network_bridge); expose_port_free_all(s->expose_ports); @@ -95,7 +97,8 @@ bool settings_private_network(Settings *s) { s->network_bridge || s->network_interfaces || s->network_macvlan || - s->network_ipvlan; + s->network_ipvlan || + s->network_veth_extra; } bool settings_network_veth(Settings *s) { @@ -269,15 +272,33 @@ int config_parse_tmpfs( return 0; } - if (settings->network_bridge) - settings->network_veth = true; + return 0; +} - if (settings->network_interfaces || - settings->network_macvlan || - settings->network_ipvlan || - settings->network_bridge || - settings->network_veth) - settings->private_network = true; +int config_parse_veth_extra( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = veth_extra_parse(&settings->network_veth_extra, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue); + return 0; + } return 0; } diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 16e8c54508..dde0d8bd45 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -69,6 +69,7 @@ typedef struct Settings { char **network_interfaces; char **network_macvlan; char **network_ipvlan; + char **network_veth_extra; ExposePort *expose_ports; } Settings; @@ -88,3 +89,4 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 4c48681f17..d2ce731c72 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -165,6 +165,7 @@ static char **arg_network_interfaces = NULL; static char **arg_network_macvlan = NULL; static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; +static char **arg_network_veth_extra = NULL; static char *arg_network_bridge = NULL; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; @@ -212,6 +213,9 @@ static void help(void) { " existing network interface to the container\n" " -n --network-veth Add a virtual Ethernet connection between host\n" " and container\n" + " --network-veth-extra=HOSTIF[:CONTAINERIF]\n" + " Add an additional virtual Ethernet link between\n" + " host and container\n" " --network-bridge=INTERFACE\n" " Add a virtual Ethernet connection between host\n" " and container and add it to an existing bridge on\n" @@ -334,6 +338,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NETWORK_MACVLAN, ARG_NETWORK_IPVLAN, ARG_NETWORK_BRIDGE, + ARG_NETWORK_VETH_EXTRA, ARG_PERSONALITY, ARG_VOLATILE, ARG_TEMPLATE, @@ -375,6 +380,7 @@ static int parse_argv(int argc, char *argv[]) { { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN }, { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN }, { "network-veth", no_argument, NULL, 'n' }, + { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA}, { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, { "personality", required_argument, NULL, ARG_PERSONALITY }, { "image", required_argument, NULL, 'i' }, @@ -449,6 +455,15 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_NETWORK; break; + case ARG_NETWORK_VETH_EXTRA: + r = veth_extra_parse(&arg_network_veth_extra, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --network-veth-extra= parameter: %s", optarg); + + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + case ARG_NETWORK_INTERFACE: if (strv_extend(&arg_network_interfaces, optarg) < 0) return log_oom(); @@ -2964,12 +2979,13 @@ static int load_settings(void) { settings->network_bridge || settings->network_interfaces || settings->network_macvlan || - settings->network_ipvlan)) { + settings->network_ipvlan || + settings->network_veth_extra)) { if (!arg_settings_trusted) log_warning("Ignoring network settings, file %s is not trusted.", p); else { - arg_network_veth = settings_private_network(settings); + arg_network_veth = settings_network_veth(settings); arg_private_network = settings_private_network(settings); strv_free(arg_network_interfaces); @@ -2984,6 +3000,10 @@ static int load_settings(void) { arg_network_ipvlan = settings->network_ipvlan; settings->network_ipvlan = NULL; + strv_free(arg_network_veth_extra); + arg_network_veth_extra = settings->network_veth_extra; + settings->network_veth_extra = NULL; + free(arg_network_bridge); arg_network_bridge = settings->network_bridge; settings->network_bridge = NULL; @@ -3409,6 +3429,10 @@ int main(int argc, char *argv[]) { } } + r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra); + if (r < 0) + goto finish; + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); if (r < 0) goto finish; @@ -3610,6 +3634,7 @@ finish: strv_free(arg_network_interfaces); strv_free(arg_network_macvlan); strv_free(arg_network_ipvlan); + strv_free(arg_network_veth_extra); strv_free(arg_parameters); custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); expose_port_free_all(arg_expose_ports); diff --git a/src/shared/generator.c b/src/shared/generator.c index 8728eb7988..9998c64416 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -64,7 +64,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "Description=File System Check on %2$s\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" - "After=%3$s\n" + "After=%3$s local-fs-pre.target\n" "Before=shutdown.target\n" "\n" "[Service]\n" diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index e1cb5d27ff..74b909d34d 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -67,42 +67,28 @@ static int specifier_instance(char specifier, void *data, void *userdata, char * } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - const char *username; - _cleanup_free_ char *tmp = NULL; - char *printed = NULL; - - assert(i); + char *t; - if (i->user) - username = i->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - switch (specifier) { - case 'u': - printed = strdup(username); - break; - case 'U': { - /* fish username from passwd */ - uid_t uid; - int r; - - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (asprintf(&printed, UID_FMT, uid) < 0) - return -ENOMEM; - break; - }} + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ + t = getusername_malloc(); + if (!t) + return -ENOMEM; - *ret = printed; + *ret = t; return 0; } +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { + + if (asprintf(ret, UID_FMT, getuid()) < 0) + return -ENOMEM; + + return 0; +} int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { @@ -114,8 +100,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) * %p: the prefix (foo) * %i: the instance (bar) - * %U the UID of the configured user or running user - * %u the username of the configured user or running user + * %U the UID of the running user + * %u the username of running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -128,7 +114,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { 'p', specifier_prefix, NULL }, { 'i', specifier_instance, NULL }, - { 'U', specifier_user_name, NULL }, + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'm', specifier_machine_id, NULL }, diff --git a/src/shared/install.c b/src/shared/install.c index b7d1d22505..9b6464ba9d 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -30,6 +30,7 @@ #include "conf-parser.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "install-printf.h" @@ -46,13 +47,21 @@ #include "unit-name.h" #include "util.h" +#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 + +typedef enum SearchFlags { + SEARCH_LOAD = 1, + SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, +} SearchFlags; + typedef struct { - OrderedHashmap *will_install; - OrderedHashmap *have_installed; + OrderedHashmap *will_process; + OrderedHashmap *have_processed; } InstallContext; static int in_search_path(const char *path, char **search) { _cleanup_free_ char *parent = NULL; + char **i; assert(path); @@ -60,7 +69,11 @@ static int in_search_path(const char *path, char **search) { if (!parent) return -ENOMEM; - return strv_contains(search, parent); + STRV_FOREACH(i, search) + if (path_equal(parent, *i)) + return true; + + return false; } static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { @@ -71,6 +84,9 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d assert(scope < _UNIT_FILE_SCOPE_MAX); assert(ret); + /* This determines where we shall create or remove our + * installation ("configuration") symlinks */ + switch (scope) { case UNIT_FILE_SYSTEM: @@ -101,9 +117,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d r = user_runtime_dir(&p); else r = user_config_home(&p); - - if (r <= 0) - return r < 0 ? r : -ENOENT; + if (r < 0) + return r; + if (r == 0) + return -ENOENT; break; @@ -118,6 +135,185 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d return 0; } +static bool is_config_path(UnitFileScope scope, const char *path) { + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(path); + + /* Checks whether the specified path is intended for + * configuration or is outside of it */ + + switch (scope) { + + case UNIT_FILE_SYSTEM: + case UNIT_FILE_GLOBAL: + return path_startswith(path, "/etc") || + path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || + path_startswith(path, "/run"); + + + case UNIT_FILE_USER: { + _cleanup_free_ char *p = NULL; + + r = user_config_home(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + p = mfree(p); + + r = user_runtime_dir(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + return false; + } + + default: + assert_not_reached("Bad scope"); + } +} + + +static int verify_root_dir(UnitFileScope scope, const char **root_dir) { + int r; + + assert(root_dir); + + /* Verifies that the specified root directory to operate on + * makes sense. Reset it to NULL if it is the root directory + * or set to empty */ + + if (isempty(*root_dir) || path_equal(*root_dir, "/")) { + *root_dir = NULL; + return 0; + } + + if (scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + r = is_dir(*root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; + + return 0; +} + +int unit_file_changes_add( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + UnitFileChange *c; + unsigned i; + + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + + *changes = c; + i = *n_changes; + + c[i].type = type; + c[i].path = strdup(path); + if (!c[i].path) + return -ENOMEM; + + path_kill_slashes(c[i].path); + + if (source) { + c[i].source = strdup(source); + if (!c[i].source) { + free(c[i].path); + return -ENOMEM; + } + + path_kill_slashes(c[i].path); + } else + c[i].source = NULL; + + *n_changes = i+1; + return 0; +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + if (!changes) + return; + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +static int create_symlink( + const char *old_path, + const char *new_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *dest = NULL; + int r; + + assert(old_path); + assert(new_path); + + /* Actually create a symlink, and remember that we did. Is + * smart enough to check if there's already a valid symlink in + * place. */ + + mkdir_parents_label(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + if (errno != EEXIST) + return -errno; + + r = readlink_malloc(new_path, &dest); + if (r < 0) + return r; + + if (path_equal(dest, old_path)) + return 0; + + if (!force) + return -EEXIST; + + r = symlink_atomic(old_path, new_path); + if (r < 0) + return r; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + + return 0; +} + static int mark_symlink_for_removal( Set **remove_symlinks_to, const char *p) { @@ -138,10 +334,12 @@ static int mark_symlink_for_removal( path_kill_slashes(n); r = set_consume(*remove_symlinks_to, n); + if (r == -EEXIST) + return 0; if (r < 0) - return r == -EEXIST ? 0 : r; + return r; - return 0; + return 1; } static int remove_marked_symlinks_fd( @@ -149,19 +347,19 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, - bool *deleted, + bool *restart, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; int r = 0; assert(remove_symlinks_to); assert(fd >= 0); assert(path); assert(config_path); - assert(deleted); + assert(restart); d = fdopendir(fd); if (!d) { @@ -171,27 +369,13 @@ static int remove_marked_symlinks_fd( rewinddir(d); - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) { - r = -errno; - break; - } - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -210,42 +394,23 @@ static int remove_marked_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes); if (q < 0 && r == 0) r = q; } else if (de->d_type == DT_LNK) { _cleanup_free_ char *p = NULL, *dest = NULL; - int q; bool found; + int q; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; - if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) && - instance_whitelist && - !strv_contains(instance_whitelist, de->d_name)) { - - _cleanup_free_ char *w = NULL; - - /* OK, the file is not listed directly - * in the whitelist, so let's check if - * the template of it might be - * listed. */ - - r = unit_name_template(de->d_name, &w); - if (r < 0) - return r; - - if (!strv_contains(instance_whitelist, w)) - continue; - } - p = path_make_absolute(de->d_name, path); if (!p) return -ENOMEM; - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); if (q < 0) { if (q == -ENOENT) continue; @@ -255,9 +420,15 @@ static int remove_marked_symlinks_fd( continue; } + /* We remove all links pointing to a file or + * path that is marked, as well as all files + * sharing the same name as a file that is + * marked. */ + found = - set_get(remove_symlinks_to, dest) || - set_get(remove_symlinks_to, basename(dest)); + set_contains(remove_symlinks_to, dest) || + set_contains(remove_symlinks_to, basename(dest)) || + set_contains(remove_symlinks_to, de->d_name); if (!found) continue; @@ -269,18 +440,15 @@ static int remove_marked_symlinks_fd( } path_kill_slashes(p); - rmdir_parents(p, config_path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + (void) rmdir_parents(p, config_path); - if (!set_get(remove_symlinks_to, p)) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); - if (q < 0) { - if (r == 0) - r = q; - } else - *deleted = true; - } + q = mark_symlink_for_removal(&remove_symlinks_to, p); + if (q < 0) + return q; + if (q > 0) + *restart = true; } } @@ -291,12 +459,11 @@ static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_close_ int fd = -1; + bool restart; int r = 0; - bool deleted; assert(config_path); @@ -309,32 +476,32 @@ static int remove_marked_symlinks( do { int q, cfd; - deleted = false; + restart = false; cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (cfd < 0) { - r = -errno; - break; - } + if (cfd < 0) + return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); if (r == 0) r = q; - } while (deleted); + } while (restart); return r; } static int find_symlinks_fd( + const char *root_dir, const char *name, int fd, const char *path, const char *config_path, bool *same_name_link) { - int r = 0; _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; assert(name); assert(fd >= 0); @@ -348,25 +515,13 @@ static int find_symlinks_fd( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - return r; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -385,7 +540,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); if (q > 0) return 1; if (r == 0) @@ -402,16 +557,27 @@ static int find_symlinks_fd( return -ENOMEM; /* Acquire symlink destination */ - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } + /* Make absolute */ + if (!path_is_absolute(dest)) { + char *x; + + x = prefix_root(root_dir, dest); + if (!x) + return -ENOMEM; + + free(dest); + dest = x; + } + /* Check if the symlink itself matches what we * are looking for */ if (path_is_absolute(name)) @@ -444,9 +610,12 @@ static int find_symlinks_fd( return 1; } } + + return r; } static int find_symlinks( + const char *root_dir, const char *name, const char *config_path, bool *same_name_link) { @@ -465,7 +634,7 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); } static int find_symlinks_in_scope( @@ -474,350 +643,59 @@ static int find_symlinks_in_scope( const char *name, UnitFileState *state) { - int r; _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL; bool same_name_link_runtime = false, same_name_link = false; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - /* First look in runtime config path */ - r = get_config_path(scope, true, root_dir, &normal_path); + /* First look in the normal config path */ + r = get_config_path(scope, false, root_dir, &normal_path); if (r < 0) return r; - r = find_symlinks(name, normal_path, &same_name_link_runtime); + r = find_symlinks(root_dir, name, normal_path, &same_name_link); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED_RUNTIME; + if (r > 0) { + *state = UNIT_FILE_ENABLED; return r; } - /* Then look in the normal config path */ - r = get_config_path(scope, false, root_dir, &runtime_path); + /* Then look in runtime config path */ + r = get_config_path(scope, true, root_dir, &runtime_path); if (r < 0) return r; - r = find_symlinks(name, runtime_path, &same_name_link); + r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED; + if (r > 0) { + *state = UNIT_FILE_ENABLED_RUNTIME; return r; } /* Hmm, we didn't find it, but maybe we found the same name * link? */ - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } else if (same_name_link) { + if (same_name_link) { *state = UNIT_FILE_LINKED; return 1; } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i; - _cleanup_free_ char *prefix = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, prefix); - if (!path) { - r = -ENOMEM; - break; - } - - if (symlink("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - - if (errno == EEXIST) { - - if (null_or_empty_path(path) > 0) - continue; - - if (force) { - if (symlink_atomic("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -int unit_file_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i, *config_path = NULL; - int r, q; - Set *remove_symlinks_to = NULL; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, config_path); - if (!path) { - r = -ENOMEM; - break; - } - - q = null_or_empty_path(path); - if (q > 0) { - if (unlink(path) < 0) - q = -errno; - else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - } - } - - if (q != -ENOENT && r == 0) - r = q; - } - - -finish: - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; - - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_link( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **i; - _cleanup_free_ char *config_path = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - char *fn; - struct stat st; - - fn = basename(*i); - - if (!path_is_absolute(*i) || - !unit_name_is_valid(fn, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - if (lstat(*i, &st) < 0) { - if (r == 0) - r = -errno; - continue; - } - - if (!S_ISREG(st.st_mode)) { - r = -ENOENT; - continue; - } - - q = in_search_path(*i, paths.unit_path); - if (q < 0) - return q; - - if (q > 0) - continue; - - path = path_make_absolute(fn, config_path); - if (!path) - return -ENOMEM; - - if (symlink(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - - if (errno == EEXIST) { - _cleanup_free_ char *dest = NULL; - - q = readlink_and_make_absolute(path, &dest); - if (q < 0 && errno != ENOENT) { - if (r == 0) - r = q; - continue; - } - - if (q >= 0 && path_equal(dest, *i)) - continue; - - if (force) { - if (symlink_atomic(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -void unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) { - free(i->path); - free(i); + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; } - hashmap_free(h); -} - -int unit_file_changes_add( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - UnitFileChange *c; - unsigned i; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - - *changes = c; - i = *n_changes; - - c[i].type = type; - c[i].path = strdup(path); - if (!c[i].path) - return -ENOMEM; - - path_kill_slashes(c[i].path); - - if (source) { - c[i].source = strdup(source); - if (!c[i].source) { - free(c[i].path); - return -ENOMEM; - } - - path_kill_slashes(c[i].path); - } else - c[i].source = NULL; - - *n_changes = i+1; return 0; } -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); +static void install_info_free(UnitFileInstallInfo *i) { - if (!changes) + if (!i) return; - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -static void install_info_free(UnitFileInstallInfo *i) { - assert(i); - free(i->name); free(i->path); strv_free(i->aliases); @@ -825,34 +703,45 @@ static void install_info_free(UnitFileInstallInfo *i) { strv_free(i->required_by); strv_free(i->also); free(i->default_instance); + free(i->symlink_target); free(i); } -static void install_info_hashmap_free(OrderedHashmap *m) { +static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { UnitFileInstallInfo *i; if (!m) - return; + return NULL; while ((i = ordered_hashmap_steal_first(m))) install_info_free(i); - ordered_hashmap_free(m); + return ordered_hashmap_free(m); } static void install_context_done(InstallContext *c) { assert(c); - install_info_hashmap_free(c->will_install); - install_info_hashmap_free(c->have_installed); + c->will_process = install_info_hashmap_free(c->will_process); + c->have_processed = install_info_hashmap_free(c->have_processed); +} + +static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { + UnitFileInstallInfo *i; + + i = ordered_hashmap_get(c->have_processed, name); + if (i) + return i; - c->will_install = c->have_installed = NULL; + return ordered_hashmap_get(c->will_process, name); } static int install_info_add( InstallContext *c, const char *name, - const char *path) { + const char *path, + UnitFileInstallInfo **ret) { + UnitFileInstallInfo *i = NULL; int r; @@ -865,17 +754,21 @@ static int install_info_add( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - if (ordered_hashmap_get(c->have_installed, name) || - ordered_hashmap_get(c->will_install, name)) + i = install_info_find(c, name); + if (i) { + if (ret) + *ret = i; return 0; + } - r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops); if (r < 0) return r; i = new0(UnitFileInstallInfo, 1); if (!i) return -ENOMEM; + i->type = _UNIT_FILE_TYPE_INVALID; i->name = strdup(name); if (!i->name) { @@ -891,30 +784,32 @@ static int install_info_add( } } - r = ordered_hashmap_put(c->will_install, i->name, i); + r = ordered_hashmap_put(c->will_process, i->name, i); if (r < 0) goto fail; + if (ret) + *ret = i; + return 0; fail: - if (i) - install_info_free(i); - + install_info_free(i); return r; } static int install_info_add_auto( InstallContext *c, - const char *name_or_path) { + const char *name_or_path, + UnitFileInstallInfo **ret) { assert(c); assert(name_or_path); if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path); + return install_info_add(c, NULL, name_or_path, ret); else - return install_info_add(c, name_or_path, NULL); + return install_info_add(c, name_or_path, NULL, ret); } static int config_parse_also( @@ -929,64 +824,33 @@ static int config_parse_also( void *data, void *userdata) { - InstallContext *c = data; UnitFileInstallInfo *i = userdata; + InstallContext *c = data; + int r; assert(filename); assert(lvalue); assert(rvalue); - for(;;) { - _cleanup_free_ char *n = NULL; - int r; - - r = extract_first_word(&rvalue, &n, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse config %s, ignoring.", rvalue); - return 0; - } + for (;;) { + _cleanup_free_ char *word = NULL; + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) + return r; if (r == 0) break; - r = install_info_add(c, n, NULL); + r = install_info_add(c, word, NULL, NULL); if (r < 0) return r; - r = strv_extend(&i->also, n); + r = strv_push(&i->also, word); if (r < 0) return r; - } - - return 0; -} -static int config_parse_user( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitFileInstallInfo *i = data; - char *printed; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = install_full_printf(i, rvalue, &printed); - if (r < 0) - return r; - - free(i->user); - i->user = printed; + word = NULL; + } return 0; } @@ -1031,9 +895,7 @@ static int unit_file_load( UnitFileInstallInfo *info, const char *path, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { const ConfigTableItem items[] = { { "Install", "Alias", config_parse_strv, 0, &info->aliases }, @@ -1041,34 +903,57 @@ static int unit_file_load( { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, { "Install", "Also", config_parse_also, 0, c }, - { "Exec", "User", config_parse_user, 0, info }, {} }; _cleanup_fclose_ FILE *f = NULL; - int fd, r; + _cleanup_close_ int fd = -1; + struct stat st; + int r; assert(c); assert(info); assert(path); - if (!isempty(root_dir)) - path = strjoina(root_dir, "/", path); + path = prefix_roota(root_dir, path); - if (!load) { - r = access(path, F_OK) ? -errno : 0; - return r; + if (!(flags & SEARCH_LOAD)) { + r = lstat(path, &st); + if (r < 0) + return -errno; + + if (null_or_empty(&st)) + info->type = UNIT_FILE_TYPE_MASKED; + else if (S_ISREG(st.st_mode)) + info->type = UNIT_FILE_TYPE_REGULAR; + else if (S_ISLNK(st.st_mode)) + return -ELOOP; + else if (S_ISDIR(st.st_mode)) + return -EISDIR; + else + return -ENOTTY; + + return 0; } - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd < 0) return -errno; + if (fstat(fd, &st) < 0) + return -errno; + if (null_or_empty(&st)) { + info->type = UNIT_FILE_MASKED; + return 0; + } + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -ENOMEM; - } + if (!f) + return -errno; + fd = -1; r = config_parse(NULL, path, f, NULL, @@ -1077,8 +962,7 @@ static int unit_file_load( if (r < 0) return r; - if (also) - *also = !strv_isempty(info->also); + info->type = UNIT_FILE_TYPE_REGULAR; return (int) strv_length(info->aliases) + @@ -1086,14 +970,73 @@ static int unit_file_load( (int) strv_length(info->required_by); } +static int unit_file_load_or_readlink( + InstallContext *c, + UnitFileInstallInfo *info, + const char *path, + const char *root_dir, + SearchFlags flags) { + + _cleanup_free_ char *np = NULL; + int r; + + r = unit_file_load(c, info, path, root_dir, flags); + if (r != -ELOOP) + return r; + + /* This is a symlink, let's read it. */ + + r = readlink_and_make_absolute_root(root_dir, path, &np); + if (r < 0) + return r; + + if (path_equal(np, "/dev/null")) + info->type = UNIT_FILE_TYPE_MASKED; + else { + const char *bn; + UnitType a, b; + + bn = basename(np); + + if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) + return -EINVAL; + } else + return -EINVAL; + + /* Enforce that the symlink destination does not + * change the unit file type. */ + + a = unit_name_to_type(info->name); + b = unit_name_to_type(bn); + if (a < 0 || b < 0 || a != b) + return -EINVAL; + + info->type = UNIT_FILE_TYPE_SYMLINK; + info->symlink_target = np; + np = NULL; + } + + return 0; +} + static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { char **p; int r; @@ -1102,8 +1045,12 @@ static int unit_file_search( assert(info); assert(paths); + /* Was this unit already loaded? */ + if (info->type != _UNIT_FILE_TYPE_INVALID) + return 0; + if (info->path) - return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also); + return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); assert(info->name); @@ -1114,14 +1061,15 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { @@ -1143,92 +1091,149 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } } return -ENOENT; } -static int unit_file_can_install( - const LookupPaths *paths, +static int install_info_follow( + InstallContext *c, + UnitFileInstallInfo *i, const char *root_dir, - const char *name, - bool allow_symlink, - bool *also) { + SearchFlags flags) { + + assert(c); + assert(i); + + if (i->type != UNIT_FILE_TYPE_SYMLINK) + return -EINVAL; + if (!i->symlink_target) + return -EINVAL; + + /* If the basename doesn't match, the caller should add a + * complete new entry for this. */ + + if (!streq(basename(i->symlink_target), i->name)) + return -EXDEV; + + free(i->path); + i->path = i->symlink_target; + i->symlink_target = NULL; + i->type = _UNIT_FILE_TYPE_INVALID; + + return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); +} + +static int install_info_traverse( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + UnitFileInstallInfo *start, + SearchFlags flags, + UnitFileInstallInfo **ret) { - _cleanup_(install_context_done) InstallContext c = {}; UnitFileInstallInfo *i; + unsigned k = 0; int r; assert(paths); - assert(name); + assert(start); + assert(c); - r = install_info_add_auto(&c, name); + r = unit_file_search(c, start, paths, root_dir, flags); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); + i = start; + while (i->type == UNIT_FILE_TYPE_SYMLINK) { + /* Follow the symlink */ - r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also); + if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) + return -ELOOP; - if (r >= 0) - r = - (int) strv_length(i->aliases) + - (int) strv_length(i->wanted_by) + - (int) strv_length(i->required_by); + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) + return -ELOOP; - return r; -} + r = install_info_follow(c, i, root_dir, flags); + if (r < 0) { + _cleanup_free_ char *buffer = NULL; + const char *bn; -static int create_symlink( - const char *old_path, - const char *new_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { + if (r != -EXDEV) + return r; - _cleanup_free_ char *dest = NULL; - int r; + /* Target has a different name, create a new + * install info object for that, and continue + * with that. */ - assert(old_path); - assert(new_path); + bn = basename(i->symlink_target); - mkdir_parents_label(new_path, 0755); + if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && + unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { - if (symlink(old_path, new_path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + _cleanup_free_ char *instance = NULL; + + r = unit_name_to_instance(i->name, &instance); + if (r < 0) + return r; + + r = unit_name_replace_instance(bn, instance, &buffer); + if (r < 0) + return r; + + bn = buffer; + } + + r = install_info_add(c, bn, NULL, &i); + if (r < 0) + return r; + + r = unit_file_search(c, i, paths, root_dir, flags); + if (r < 0) + return r; + } + + /* Try again, with the new target we found. */ } - if (errno != EEXIST) - return -errno; + if (ret) + *ret = i; - r = readlink_and_make_absolute(new_path, &dest); - if (r < 0) - return r; + return 0; +} - if (path_equal(dest, old_path)) - return 0; +static int install_info_discover( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret) { - if (!force) - return -EEXIST; + UnitFileInstallInfo *i; + int r; - r = symlink_atomic(old_path, new_path); + assert(c); + assert(paths); + assert(name); + + r = install_info_add_auto(c, name, &i); if (r < 0) return r; - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - - return 0; + return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); } static int install_info_symlink_alias( @@ -1363,6 +1368,9 @@ static int install_info_apply( assert(paths); assert(config_path); + if (i->type != UNIT_FILE_TYPE_REGULAR) + return 0; + r = install_info_symlink_alias(i, config_path, force, changes, n_changes); q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); @@ -1381,53 +1389,59 @@ static int install_info_apply( } static int install_context_apply( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, const char *config_path, const char *root_dir, bool force, + SearchFlags flags, UnitFileChange **changes, unsigned *n_changes) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); assert(config_path); - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); - if (r < 0) - return r; + if (ordered_hashmap_isempty(c->will_process)) + return 0; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); - if (r < 0) - return r; - } + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + while ((i = ordered_hashmap_first(c->will_process))) { + int q; - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q < 0) { - if (r >= 0) - r = q; + q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + if (q < 0) + return q; + r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + if (r < 0) return r; - } else if (r >= 0) - r += q; + + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); - if (r >= 0 && q < 0) - r = q; + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } } return r; } static int install_context_mark_for_removal( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, @@ -1435,7 +1449,7 @@ static int install_context_mark_for_removal( const char *root_dir) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); @@ -1443,87 +1457,182 @@ static int install_context_mark_for_removal( /* Marks all items for removal */ - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); + if (ordered_hashmap_isempty(c->will_process)) + return 0; + + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; + + while ((i = ordered_hashmap_first(c->will_process))) { + + r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); if (r < 0) return r; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); + r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r < 0) return r; - } - r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q == -ENOENT) { - /* do nothing */ - } else if (q < 0) { - if (r >= 0) - r = q; + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; + r = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r < 0) return r; - } else if (r >= 0) - r += q; - - if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { - char *unit_file; - - if (i->path) { - unit_file = basename(i->path); - - if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE)) - /* unit file named as instance exists, thus all symlinks - * pointing to it will be removed */ - q = mark_symlink_for_removal(remove_symlinks_to, i->name); - else - /* does not exist, thus we will mark for removal symlinks - * to template unit file */ - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - } else { - /* If i->path is not set, it means that we didn't actually find - * the unit file. But we can still remove symlinks to the - * nonexistent template. */ - r = unit_name_template(i->name, &unit_file); - if (r < 0) - return r; + } - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - free(unit_file); - } - } else - q = mark_symlink_for_removal(remove_symlinks_to, i->name); + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *prefix = NULL; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); - if (r >= 0 && q < 0) + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &prefix); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + int q; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, prefix); + if (!path) + return -ENOMEM; + + q = create_symlink("/dev/null", path, force, changes, n_changes); + if (q < 0 && r >= 0) r = q; } return r; } -int unit_file_add_dependency( +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + _cleanup_free_ char *config_path = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + r = null_or_empty_path(path); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + if (unlink(path) < 0) { + if (errno != -ENOENT && r >= 0) + r = -errno; + } else { + q = mark_symlink_for_removal(&remove_symlinks_to, path); + if (q < 0) + return q; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + } + } + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + +int unit_file_link( UnitFileScope scope, bool runtime, const char *root_dir, char **files, - char *target, - UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; char **i; - int r; - UnitFileInstallInfo *info; + int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1533,55 +1642,135 @@ int unit_file_add_dependency( return r; STRV_FOREACH(i, files) { - UnitFileState state; + _cleanup_free_ char *full = NULL; + struct stat st; + char *fn; - state = unit_file_get_state(scope, root_dir, *i); - if (state < 0) - return log_error_errno(state, "Failed to get unit file state for %s: %m", *i); + if (!path_is_absolute(*i)) + return -EINVAL; - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } + fn = basename(*i); + if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) + return -EINVAL; - r = install_info_add_auto(&c, *i); - if (r < 0) - return r; + full = prefix_root(root_dir, *i); + if (!full) + return -ENOMEM; + + if (lstat(full, &st) < 0) + return -errno; + if (S_ISLNK(st.st_mode)) + return -ELOOP; + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; + + q = in_search_path(*i, paths.unit_path); + if (q < 0) + return q; + if (q > 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; } - if (!ordered_hashmap_isempty(c.will_install)) { - r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops); - if (r < 0) - return r; + strv_uniq(todo); - r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install)); - if (r < 0) - return r; + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(basename(*i), config_path); + if (!path) + return -ENOMEM; + + q = create_symlink(*i, path, force, changes, n_changes); + if (q < 0 && r >= 0) + r = q; } - while ((info = ordered_hashmap_first(c.will_install))) { - assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0); + return r; +} + +int unit_file_add_dependency( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + const char *target, + UnitDependency dep, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i, *target_info; + char **f; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(target); + + if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES)) + return -EINVAL; - r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL); + if (!unit_name_is_valid(target, UNIT_NAME_ANY)) + return -EINVAL; + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = lookup_paths_init_from_scope(&paths, scope, root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + return r; + + r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + if (r < 0) + return r; + if (target_info->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(target_info->type == UNIT_FILE_TYPE_REGULAR); + + STRV_FOREACH(f, files) { + char ***l; + + r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); + + /* We didn't actually load anything from the unit + * file, but instead just add in our new symlink to + * create. */ if (dep == UNIT_WANTS) - r = strv_extend(&info->wanted_by, target); - else if (dep == UNIT_REQUIRES) - r = strv_extend(&info->required_by, target); + l = &i->wanted_by; else - r = -EINVAL; + l = &i->required_by; - if (r < 0) - return r; - - r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes); - if (r < 0) - return r; + strv_free(*l); + *l = strv_new(target_info->name, NULL); + if (!*l) + return -ENOMEM; } - return 0; + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1595,13 +1784,18 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i; + char **f; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1610,29 +1804,22 @@ int unit_file_enable( if (r < 0) return r; - STRV_FOREACH(i, files) { - UnitFileState state; - - /* We only want to know if this unit is masked, so we ignore - * errors from unit_file_get_state, deferring other checks. - * This allows templated units to be enabled on the fly. */ - state = unit_file_get_state(scope, root_dir, *i); - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } - - r = install_info_add_auto(&c, *i); + STRV_FOREACH(f, files) { + r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); } /* This will return the number of symlink rules that were - supposed to be created, not the ones actually created. This is - useful to determine whether the passed files had any - installation data at all. */ + supposed to be created, not the ones actually created. This + is useful to determine whether the passed files had any + installation data at all. */ - return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1645,14 +1832,18 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - int r, q; + char **i; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1662,18 +1853,19 @@ int unit_file_disable( return r; STRV_FOREACH(i, files) { - r = install_info_add_auto(&c, *i); + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_add(&c, *i, NULL, NULL); if (r < 0) return r; } - r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r >= 0) - r = q; + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + if (r < 0) + return r; - return r; + return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); } int unit_file_reenable( @@ -1684,21 +1876,30 @@ int unit_file_reenable( bool force, UnitFileChange **changes, unsigned *n_changes) { + + char **n; int r; + size_t l, i; + + /* First, we invoke the disable command with only the basename... */ + l = strv_length(files); + n = newa(char*, l+1); + for (i = 0; i < l; i++) + n[i] = basename(files[i]); + n[i] = NULL; - r = unit_file_disable(scope, runtime, root_dir, files, - changes, n_changes); + r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes); if (r < 0) return r; - return unit_file_enable(scope, runtime, root_dir, files, force, - changes, n_changes); + /* But the enable command with the full name */ + return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes); } int unit_file_set_default( UnitFileScope scope, const char *root_dir, - const char *file, + const char *name, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1706,42 +1907,40 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; - char *path; + UnitFileInstallInfo *i; + const char *path; int r; - UnitFileInstallInfo *i = NULL; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(file); + assert(name); - if (unit_name_to_type(file) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) + return -EINVAL; + if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - r = get_config_path(scope, false, root_dir, &config_path); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - r = install_info_add_auto(&c, file); + r = get_config_path(scope, false, root_dir, &config_path); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); - - r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL); + r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET); - r = create_symlink(i->path, path, force, changes, n_changes); - if (r < 0) - return r; - - return 0; + return create_symlink(i->path, path, force, changes, n_changes); } int unit_file_get_default( @@ -1750,126 +1949,101 @@ int unit_file_get_default( char **name) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **p; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + char *n; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - STRV_FOREACH(p, paths.unit_path) { - _cleanup_free_ char *path = NULL, *tmp = NULL; - char *n; - - path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET); - if (!path) - return -ENOMEM; - - r = readlink_malloc(path, &tmp); - if (r == -ENOENT) - continue; - else if (r == -EINVAL) - /* not a symlink */ - n = strdup(SPECIAL_DEFAULT_TARGET); - else if (r < 0) - return r; - else - n = strdup(basename(tmp)); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); + if (r < 0) + return r; - if (!n) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; - *name = n; - return 0; - } + n = strdup(i->name); + if (!n) + return -ENOMEM; - return -ENOENT; + *name = n; + return 0; } -UnitFileState unit_file_lookup_state( +int unit_file_lookup_state( UnitFileScope scope, const char *root_dir, const LookupPaths *paths, - const char *name) { + const char *name, + UnitFileState *ret) { - UnitFileState state = _UNIT_FILE_STATE_INVALID; - char **i; - _cleanup_free_ char *path = NULL; - int r = 0; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + UnitFileState state; + int r; assert(paths); + assert(name); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - STRV_FOREACH(i, paths->unit_path) { - struct stat st; - char *partial; - bool also = false; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; - free(path); - path = path_join(root_dir, *i, name); - if (!path) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; - if (root_dir) - partial = path + strlen(root_dir); - else - partial = path; - - /* - * Search for a unit file in our default paths, to - * be sure, that there are no broken symlinks. - */ - if (lstat(path, &st) < 0) { - r = -errno; - if (errno != ENOENT) - return r; + /* Shortcut things, if the caller just wants to know if this unit exists. */ + if (!ret) + return 0; - if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE)) - continue; - } else { - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) - return -ENOENT; + switch (i->type) { - r = null_or_empty_path(path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - return state; - } - } + case UNIT_FILE_TYPE_MASKED: + state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + break; - r = find_symlinks_in_scope(scope, root_dir, name, &state); + case UNIT_FILE_TYPE_REGULAR: + r = find_symlinks_in_scope(scope, root_dir, i->name, &state); if (r < 0) return r; - else if (r > 0) - return state; - - r = unit_file_can_install(paths, root_dir, partial, true, &also); - if (r < 0 && errno != ENOENT) - return r; - else if (r > 0) - return UNIT_FILE_DISABLED; - else if (r == 0) { - if (also) - return UNIT_FILE_INDIRECT; - return UNIT_FILE_STATIC; + if (r == 0) { + if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i)) + state = UNIT_FILE_DISABLED; + else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i)) + state = UNIT_FILE_INDIRECT; + else + state = UNIT_FILE_STATIC; } + + break; + + default: + assert_not_reached("Unexpect unit file type."); } - return r < 0 ? r : state; + *ret = state; + return 0; } -UnitFileState unit_file_get_state( +int unit_file_get_state( UnitFileScope scope, const char *root_dir, - const char *name) { + const char *name, + UnitFileState *ret) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; int r; @@ -1878,14 +2052,15 @@ UnitFileState unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name); + return unit_file_lookup_state(scope, root_dir, &paths, name, ret); } int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { @@ -1897,6 +2072,13 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, "/etc/systemd/system-preset", @@ -1913,13 +2095,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char "/usr/lib/systemd/user-preset", NULL); else - return 1; + return 1; /* Default is "enable" */ if (r < 0) return r; STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; + char line[LINE_MAX]; f = fopen(*p, "re"); if (!f) { @@ -1929,39 +2112,38 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return -errno; } - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) - break; + FOREACH_LINE(line, f, return -errno) { + const char *parameter; + char *l; l = strstrip(line); - if (!*l) - continue; - if (strchr(COMMENTS "\n", *l)) + if (isempty(l)) + continue; + if (strchr(COMMENTS, *l)) continue; - if (first_word(l, "enable")) { - l += 6; - l += strspn(l, WHITESPACE); - - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "enable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says enable %s.", name); return 1; } - } else if (first_word(l, "disable")) { - l += 7; - l += strspn(l, WHITESPACE); + continue; + } - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "disable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says disable %s.", name); return 0; } - } else - log_debug("Couldn't parse line '%s'", l); + continue; + } + + log_debug("Couldn't parse line '%s'", l); } } @@ -1970,6 +2152,86 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return 1; } +static int execute_preset( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + const LookupPaths *paths, + const char *config_path, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r; + + assert(plus); + assert(minus); + assert(paths); + assert(config_path); + + if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir); + if (r < 0) + return r; + + r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + } else + r = 0; + + if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { + int q; + + /* Returns number of symlinks that where supposed to be installed. */ + q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } + } + + return r; +} + +static int preset_prepare_one( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + LookupPaths *paths, + const char *root_dir, + UnitFilePresetMode mode, + const char *name) { + + UnitFileInstallInfo *i; + int r; + + if (install_info_find(plus, name) || + install_info_find(minus, name)) + return 0; + + r = unit_file_query_preset(scope, root_dir, name); + if (r < 0) + return r; + + if (r > 0) { + r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + } else + r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + + return r; +} + int unit_file_preset( UnitFileScope scope, bool runtime, @@ -1984,12 +2246,16 @@ int unit_file_preset( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1999,44 +2265,15 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = unit_file_query_preset(scope, root_dir, *i); + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); if (r < 0) return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, *i); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, *i); - else - r = 0; - if (r < 0) - return r; - } - - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; } - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2052,12 +2289,16 @@ int unit_file_preset_all( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -2069,6 +2310,7 @@ int unit_file_preset_all( STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; _cleanup_free_ char *units_dir; + struct dirent *de; units_dir = path_join(root_dir, *i, NULL); if (!units_dir) @@ -2082,62 +2324,23 @@ int unit_file_preset_all( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; dirent_ensure_type(d, de); - if (de->d_type != DT_REG) + if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = unit_file_query_preset(scope, root_dir, de->d_name); - if (r < 0) - return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, de->d_name); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, de->d_name); - else - r = 0; + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); if (r < 0) return r; } } - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL); - if (r == 0) - r = q; - } - - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2148,6 +2351,15 @@ static void unit_file_list_free_one(UnitFileList *f) { free(f); } +Hashmap* unit_file_list_free(Hashmap *h) { + UnitFileList *i; + + while ((i = hashmap_steal_first(h))) + unit_file_list_free_one(i); + + return hashmap_free(h); +} + DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( @@ -2163,14 +2375,9 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; - - if (root_dir) { - r = access(root_dir, F_OK); - if (r < 0) - return -errno; - } + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) @@ -2179,6 +2386,7 @@ int unit_file_get_list( STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; _cleanup_free_ char *units_dir; + struct dirent *de; units_dir = path_join(root_dir, *i, NULL); if (!units_dir) @@ -2192,22 +2400,8 @@ int unit_file_get_list( return -errno; } - for (;;) { + FOREACH_DIRENT(de, d, return -errno) { _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; - struct dirent *de; - _cleanup_free_ char *path = NULL; - bool also = false; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; @@ -2228,44 +2422,14 @@ int unit_file_get_list( if (!f->path) return -ENOMEM; - r = null_or_empty_path(f->path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - f->state = - path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - goto found; - } - - r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); + r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); if (r < 0) - return r; - else if (r > 0) { - f->state = UNIT_FILE_ENABLED; - goto found; - } - - path = path_make_absolute(de->d_name, *i); - if (!path) - return -ENOMEM; + f->state = UNIT_FILE_BAD; - r = unit_file_can_install(&paths, root_dir, path, true, &also); - if (r == -EINVAL || /* Invalid setting? */ - r == -EBADMSG || /* Invalid format? */ - r == -ENOENT /* Included file not found? */) - f->state = UNIT_FILE_INVALID; - else if (r < 0) - return r; - else if (r > 0) - f->state = UNIT_FILE_DISABLED; - else - f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC; - - found: r = hashmap_put(h, basename(f->path), f); if (r < 0) return r; + f = NULL; /* prevent cleanup */ } } @@ -2283,7 +2447,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_INVALID] = "invalid", + [UNIT_FILE_BAD] = "bad", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); diff --git a/src/shared/install.h b/src/shared/install.h index a9d77dd91b..45a417df92 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -25,13 +25,15 @@ typedef enum UnitFileScope UnitFileScope; typedef enum UnitFileState UnitFileState; typedef enum UnitFilePresetMode UnitFilePresetMode; typedef enum UnitFileChangeType UnitFileChangeType; +typedef enum UnitFileType UnitFileType; typedef struct UnitFileChange UnitFileChange; typedef struct UnitFileList UnitFileList; typedef struct UnitFileInstallInfo UnitFileInstallInfo; #include "hashmap.h" -#include "unit-name.h" #include "path-lookup.h" +#include "strv.h" +#include "unit-name.h" enum UnitFileScope { UNIT_FILE_SYSTEM, @@ -51,7 +53,7 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, - UNIT_FILE_INVALID, + UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 }; @@ -82,10 +84,17 @@ struct UnitFileList { UnitFileState state; }; +enum UnitFileType { + UNIT_FILE_TYPE_REGULAR, + UNIT_FILE_TYPE_SYMLINK, + UNIT_FILE_TYPE_MASKED, + _UNIT_FILE_TYPE_MAX, + _UNIT_FILE_TYPE_INVALID = -1, +}; + struct UnitFileInstallInfo { char *name; char *path; - char *user; char **aliases; char **wanted_by; @@ -93,8 +102,26 @@ struct UnitFileInstallInfo { char **also; char *default_instance; + + UnitFileType type; + + char *symlink_target; }; +static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->aliases) || + !strv_isempty(i->wanted_by) || + !strv_isempty(i->required_by); +} + +static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->also); +} + int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); @@ -105,21 +132,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); -int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); - -UnitFileState unit_file_lookup_state( - UnitFileScope scope, - const char *root_dir, - const LookupPaths *paths, - const char *name); -UnitFileState unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *filename); +int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); + +int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret); +int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); +Hashmap* unit_file_list_free(Hashmap *h); -void unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 764c293f08..51b82d57db 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1335,7 +1335,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { UNIT_FILE_MASKED, UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_DISABLED, - UNIT_FILE_INVALID)) { + UNIT_FILE_BAD)) { on = ansi_highlight_red(); off = ansi_normal(); } else if (u->state == UNIT_FILE_ENABLED) { @@ -5433,10 +5433,10 @@ static int enable_unit(int argc, char *argv[], void *userdata) { else assert_not_reached("Unknown verb"); - if (r < 0) { - log_error_errno(r, "Operation failed: %m"); - goto finish; - } + if (r == -ESHUTDOWN) + return log_error_errno(r, "Unit file is masked."); + if (r < 0) + return log_error_errno(r, "Operation failed: %m"); if (!arg_quiet) dump_unit_file_changes(changes, n_changes); @@ -5553,7 +5553,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) - return r; + goto finish; new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop"); for (i = 0; i < n_changes; i++) @@ -5599,7 +5599,8 @@ static int add_dependency(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - + if (r == -ESHUTDOWN) + return log_error_errno(r, "Unit file is masked."); if (r < 0) return log_error_errno(r, "Can't add dependency: %m"); @@ -5736,8 +5737,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { STRV_FOREACH(name, names) { UnitFileState state; - state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) + r = unit_file_get_state(arg_scope, arg_root, *name, &state); + if (r < 0) return log_error_errno(state, "Failed to get unit file state for %s: %m", *name); if (IN_SET(state, diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 042be97840..5075548507 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -742,6 +742,7 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { char **path; + int r; assert(lp); assert(all_services); @@ -761,7 +762,6 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { _cleanup_free_ char *fpath = NULL, *name = NULL; _cleanup_(free_sysvstubp) SysvStub *service = NULL; struct stat st; - int r; if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); @@ -781,8 +781,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) { - log_debug("Native unit for %s already exists, skipping", name); + r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL); + if (r < 0 && r != -ENOENT) { + log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); + continue; + } else if (r >= 0) { + log_debug("Native unit for %s already exists, skipping.", name); continue; } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c new file mode 100644 index 0000000000..08fde94f7f --- /dev/null +++ b/src/test/test-install-root.c @@ -0,0 +1,665 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + 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 "alloc-util.h" +#include "fileio.h" +#include "install.h" +#include "mkdir.h" +#include "rm-rf.h" +#include "string-util.h" + +static void test_basic_mask_and_enable(const char *root) { + const char *p; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/a.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/b.service"); + assert_se(symlink("a.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/c.service"); + assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/d.service"); + assert_se(symlink("c.service", p) >= 0); + + /* This one is interesting, as d follows a relative, then an absolute symlink */ + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_mask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/dev/null")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + assert_se(streq(changes[0].path, p)); + + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); + + /* Enabling a masked unit should fail! */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* Enabling it again should succeed but be a NOP */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + /* Disabling a disabled unit must suceed but be a NOP */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + /* Let's enable this indirectly via a symlink */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("d.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* Let's try to reenable */ + + assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("b.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + assert_se(changes[1].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[1].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); +} + +static void test_linked_units(const char *root) { + const char *p, *q; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0, i; + + /* + * We'll test three cases here: + * + * a) a unit file in /opt, that we use "systemctl link" and + * "systemctl enable" on to make it available to the system + * + * b) a unit file in /opt, that is statically linked into + * /usr/lib/systemd/system, that "enable" should work on + * correctly. + * + * c) a unit file in /opt, that is linked into + * /etc/systemd/system, and where "enable" should result in + * -ELOOP, since using information from /etc to generate + * information in /etc should not be allowed. + */ + + p = strjoina(root, "/opt/linked.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/opt/linked2.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/opt/linked3.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/linked2.service"); + assert_se(symlink("/opt/linked2.service", p) >= 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service"); + assert_se(symlink("/opt/linked3.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); + + /* First, let's link the unit into the search path */ + assert_se(unit_file_link(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/opt/linked.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); + + /* Let's unlink it from the search path again */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + + /* Now, let's not just link it, but also enable it */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + for (i = 0 ; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[i].source, "/opt/linked.service")); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* And let's unlink it again */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + for (i = 0; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_UNLINK); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked2.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service"); + for (i = 0 ; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[i].source, "/opt/linked2.service")); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked3.service"), false, &changes, &n_changes) == -ELOOP); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_default(const char *root) { + _cleanup_free_ char *def = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + + p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/test-default.target"); + assert_se(symlink("test-default-real.target", p) >= 0); + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + + assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + + assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "test-default.target", false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/default.target"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0); + assert_se(streq_ptr(def, "test-default-real.target")); +} + +static void test_add_dependency(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + + p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target"); + assert_se(symlink("real-add-dependency-test-target.target", p) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service"); + assert_se(symlink("real-add-dependency-test-service.service", p) >= 0); + + assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_template_enable(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + UnitFileState state; + const char *p; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/template@.service"); + assert_se(write_string_file(p, + "[Install]\n" + "DefaultInstance=def\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service"); + assert_se(symlink("template@.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), false, &changes, &n_changes) >= 0); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template-symlink@quux.service"), false, &changes, &n_changes) >= 0); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); +} + +static void test_indirect(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + UnitFileState state; + const char *p; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/indirecta.service"); + assert_se(write_string_file(p, + "[Install]\n" + "Also=indirectb.service\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/indirectb.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/indirectc.service"); + assert_se(symlink("indirecta.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_preset_and_list(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0, i; + const char *p, *q; + UnitFileState state; + bool got_yes = false, got_no = false; + Iterator j; + UnitFileList *fl; + Hashmap *h; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); + assert_se(write_string_file(p, + "enable *-yes.*\n" + "disable *\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, false, root, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + + assert_se(n_changes > 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + + for (i = 0; i < n_changes; i++) { + + if (changes[i].type == UNIT_FILE_SYMLINK) { + assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[i].path, p)); + } else + assert_se(changes[i].type == UNIT_FILE_UNLINK); + } + + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(h = hashmap_new(&string_hash_ops)); + assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); + q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); + + HASHMAP_FOREACH(fl, h, j) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0); + assert_se(fl->state == state); + + if (streq(fl->path, p)) { + got_yes = true; + assert_se(fl->state == UNIT_FILE_ENABLED); + } else if (streq(fl->path, q)) { + got_no = true; + assert_se(fl->state == UNIT_FILE_DISABLED); + } else + assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT)); + } + + unit_file_list_free(h); + + assert_se(got_yes && got_no); +} + +int main(int argc, char *argv[]) { + char root[] = "/tmp/rootXXXXXX"; + const char *p; + + assert_se(mkdtemp(root)); + + p = strjoina(root, "/usr/lib/systemd/system/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/run/systemd/system/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/opt/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/"); + assert_se(mkdir_p(p, 0755) >= 0); + + test_basic_mask_and_enable(root); + test_linked_units(root); + test_default(root); + test_add_dependency(root); + test_template_enable(root); + test_indirect(root); + test_preset_and_list(root); + + assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + return 0; +} diff --git a/src/test/test-install.c b/src/test/test-install.c index 5ee52e64cb..359b262347 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -46,17 +46,19 @@ int main(int argc, char* argv[]) { const char *const files2[] = { "/home/lennart/test.service", NULL }; UnitFileChange *changes = NULL; unsigned n_changes = 0; + UnitFileState state = 0; h = hashmap_new(&string_hash_ops); r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); assert_se(r == 0); HASHMAP_FOREACH(p, h, i) { - UnitFileState s; + UnitFileState s = _UNIT_FILE_STATE_INVALID; - s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path)); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s); - assert_se(p->state == s); + assert_se((r < 0 && p->state == UNIT_FILE_BAD) || + (p->state == s)); fprintf(stderr, "%s (%s)\n", p->path, @@ -78,7 +80,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable"); @@ -91,7 +95,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("mask"); changes = NULL; @@ -106,7 +112,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("unmask"); changes = NULL; @@ -121,7 +129,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("mask"); changes = NULL; @@ -133,7 +143,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("disable"); changes = NULL; @@ -148,7 +160,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("umask"); changes = NULL; @@ -160,7 +174,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("enable files2"); changes = NULL; @@ -172,19 +188,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("link files2"); changes = NULL; @@ -196,19 +215,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_LINKED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("link files2"); changes = NULL; @@ -220,7 +242,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_LINKED); log_error("reenable files2"); changes = NULL; @@ -232,19 +256,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("preset files"); changes = NULL; n_changes = 0; @@ -255,7 +282,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); return 0; } diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index a8e5b6feee..c3973a316e 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -40,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "user-util.h" #include "util.h" static int test_unit_file_get_set(void) { @@ -558,76 +559,66 @@ static void test_load_env_file_5(void) { static void test_install_printf(void) { char name[] = "name.service", - path[] = "/run/systemd/system/name.service", - user[] = "xxxx-no-such-user"; - UnitFileInstallInfo i = {name, path, user}; - UnitFileInstallInfo i2 = {name, path, NULL}; + path[] = "/run/systemd/system/name.service"; + UnitFileInstallInfo i = { .name = name, .path = path, }; + UnitFileInstallInfo i2 = { .name= name, .path = path, }; char name3[] = "name@inst.service", path3[] = "/run/systemd/system/name.service"; - UnitFileInstallInfo i3 = {name3, path3, user}; - UnitFileInstallInfo i4 = {name3, path3, NULL}; + UnitFileInstallInfo i3 = { .name = name3, .path = path3, }; + UnitFileInstallInfo i4 = { .name = name3, .path = path3, }; - _cleanup_free_ char *mid, *bid, *host; + _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL; assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid); assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid); assert_se((host = gethostname_malloc())); + assert_se((user = getusername_malloc())); + assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0); #define expect(src, pattern, result) \ do { \ _cleanup_free_ char *t = NULL; \ _cleanup_free_ char \ *d1 = strdup(i.name), \ - *d2 = strdup(i.path), \ - *d3 = strdup(i.user); \ + *d2 = strdup(i.path); \ assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \ memzero(i.name, strlen(i.name)); \ memzero(i.path, strlen(i.path)); \ - memzero(i.user, strlen(i.user)); \ - assert_se(d1 && d2 && d3); \ + assert_se(d1 && d2); \ if (result) { \ printf("%s\n", t); \ - assert_se(streq(t, result)); \ - } else assert_se(t == NULL); \ + assert_se(streq(t, result)); \ + } else assert_se(t == NULL); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ - strcpy(i.user, d3); \ } while(false) - assert_se(setenv("USER", "root", 1) == 0); - expect(i, "%n", "name.service"); expect(i, "%N", "name"); expect(i, "%p", "name"); expect(i, "%i", ""); - expect(i, "%u", "xxxx-no-such-user"); - - DISABLE_WARNING_NONNULL; - expect(i, "%U", NULL); - REENABLE_WARNING; + expect(i, "%u", user); + expect(i, "%U", uid); expect(i, "%m", mid); expect(i, "%b", bid); expect(i, "%H", host); - expect(i2, "%u", "root"); - expect(i2, "%U", "0"); + expect(i2, "%u", user); + expect(i2, "%U", uid); expect(i3, "%n", "name@inst.service"); expect(i3, "%N", "name@inst"); expect(i3, "%p", "name"); - expect(i3, "%u", "xxxx-no-such-user"); - - DISABLE_WARNING_NONNULL; - expect(i3, "%U", NULL); - REENABLE_WARNING; + expect(i3, "%u", user); + expect(i3, "%U", uid); expect(i3, "%m", mid); expect(i3, "%b", bid); expect(i3, "%H", host); - expect(i4, "%u", "root"); - expect(i4, "%U", "0"); + expect(i4, "%u", user); + expect(i4, "%U", uid); } static uint64_t make_cap(int cap) { diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 9db7853dd4..842ca40102 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -37,6 +37,7 @@ #include "unit-name.h" #include "unit-printf.h" #include "unit.h" +#include "user-util.h" #include "util.h" static void test_unit_name_is_valid(void) { @@ -193,15 +194,15 @@ static int test_unit_printf(void) { Unit *u, *u2; int r; - _cleanup_free_ char *mid, *bid, *host, *root_uid; - struct passwd *root; + _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL; assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid); assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid); - assert_se((host = gethostname_malloc())); - - assert_se((root = getpwnam("root"))); - assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0); + assert_se(host = gethostname_malloc()); + assert_se(user = getusername_malloc()); + assert_se(asprintf(&uid, UID_FMT, getuid())); + assert_se(get_home_dir(&home) >= 0); + assert_se(get_shell(&shell) >= 0); r = manager_new(MANAGER_USER, true, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) { @@ -222,8 +223,6 @@ static int test_unit_printf(void) { assert_se(streq(t, expected)); \ } - assert_se(setenv("USER", "root", 1) == 0); - assert_se(setenv("HOME", "/root", 1) == 0); assert_se(setenv("XDG_RUNTIME_DIR", "/run/user/1/", 1) == 0); assert_se(u = unit_new(m, sizeof(Service))); @@ -242,9 +241,9 @@ static int test_unit_printf(void) { expect(u, "%p", "blah"); expect(u, "%P", "blah"); expect(u, "%i", ""); - expect(u, "%u", root->pw_name); - expect(u, "%U", root_uid); - expect(u, "%h", root->pw_dir); + expect(u, "%u", user); + expect(u, "%U", uid); + expect(u, "%h", home); expect(u, "%m", mid); expect(u, "%b", bid); expect(u, "%H", host); @@ -262,9 +261,9 @@ static int test_unit_printf(void) { expect(u2, "%P", "blah"); expect(u2, "%i", "foo-foo"); expect(u2, "%I", "foo/foo"); - expect(u2, "%u", root->pw_name); - expect(u2, "%U", root_uid); - expect(u2, "%h", root->pw_dir); + expect(u2, "%u", user); + expect(u2, "%U", uid); + expect(u2, "%h", home); expect(u2, "%m", mid); expect(u2, "%b", bid); expect(u2, "%H", host); |