diff options
159 files changed, 4088 insertions, 910 deletions
diff --git a/.gitignore b/.gitignore index 02ba86ef6f..c7eb14452d 100644 --- a/.gitignore +++ b/.gitignore @@ -240,6 +240,7 @@ /test-ns /test-nss /test-parse-util +/test-patch-uid /test-path /test-path-lookup /test-path-util @@ -16,5 +16,5 @@ set shiftwidth=8 set expandtab set makeprg=GCC_COLORS=\ make set tw=79 -au FileType xml set tw=119 +au BufRead,BufNewFile *.xml set tw=119 shiftwidth=2 smarttab au FileType c set tw=119 diff --git a/CODING_STYLE b/CODING_STYLE index c2b2e56d5d..b689355c9a 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -240,6 +240,11 @@ unlink("/foo/bar/baz"); + Don't cast function calls to (void) that return no error + conditions. Specifically, the various xyz_unref() calls that return a NULL + object shouldn't be cast to (void), since not using the return value does not + hide any errors. + - Don't invoke exit(), ever. It is not replacement for proper error handling. Please escalate errors up your call chain, and use normal "return" to exit from the main function of a process. If you diff --git a/Makefile-man.am b/Makefile-man.am index 7514e6a24f..d5b328d267 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -242,6 +242,7 @@ MANPAGES_ALIAS += \ man/SD_JOURNAL_INVALIDATE.3 \ man/SD_JOURNAL_LOCAL_ONLY.3 \ man/SD_JOURNAL_NOP.3 \ + man/SD_JOURNAL_OS_ROOT.3 \ man/SD_JOURNAL_RUNTIME_ONLY.3 \ man/SD_JOURNAL_SUPPRESS_LOCATION.3 \ man/SD_JOURNAL_SYSTEM.3 \ @@ -388,9 +389,10 @@ MANPAGES_ALIAS += \ man/sd_journal_get_timeout.3 \ man/sd_journal_has_persistent_files.3 \ man/sd_journal_next_skip.3 \ - man/sd_journal_open_container.3 \ man/sd_journal_open_directory.3 \ + man/sd_journal_open_directory_fd.3 \ man/sd_journal_open_files.3 \ + man/sd_journal_open_files_fd.3 \ man/sd_journal_perror.3 \ man/sd_journal_previous.3 \ man/sd_journal_previous_skip.3 \ @@ -571,6 +573,7 @@ man/SD_JOURNAL_FOREACH_UNIQUE.3: man/sd_journal_query_unique.3 man/SD_JOURNAL_INVALIDATE.3: man/sd_journal_get_fd.3 man/SD_JOURNAL_LOCAL_ONLY.3: man/sd_journal_open.3 man/SD_JOURNAL_NOP.3: man/sd_journal_get_fd.3 +man/SD_JOURNAL_OS_ROOT.3: man/sd_journal_open.3 man/SD_JOURNAL_RUNTIME_ONLY.3: man/sd_journal_open.3 man/SD_JOURNAL_SUPPRESS_LOCATION.3: man/sd_journal_print.3 man/SD_JOURNAL_SYSTEM.3: man/sd_journal_open.3 @@ -717,9 +720,10 @@ man/sd_journal_get_monotonic_usec.3: man/sd_journal_get_realtime_usec.3 man/sd_journal_get_timeout.3: man/sd_journal_get_fd.3 man/sd_journal_has_persistent_files.3: man/sd_journal_has_runtime_files.3 man/sd_journal_next_skip.3: man/sd_journal_next.3 -man/sd_journal_open_container.3: man/sd_journal_open.3 man/sd_journal_open_directory.3: man/sd_journal_open.3 +man/sd_journal_open_directory_fd.3: man/sd_journal_open.3 man/sd_journal_open_files.3: man/sd_journal_open.3 +man/sd_journal_open_files_fd.3: man/sd_journal_open.3 man/sd_journal_perror.3: man/sd_journal_print.3 man/sd_journal_previous.3: man/sd_journal_next.3 man/sd_journal_previous_skip.3: man/sd_journal_next.3 @@ -1034,6 +1038,9 @@ man/SD_JOURNAL_LOCAL_ONLY.html: man/sd_journal_open.html man/SD_JOURNAL_NOP.html: man/sd_journal_get_fd.html $(html-alias) +man/SD_JOURNAL_OS_ROOT.html: man/sd_journal_open.html + $(html-alias) + man/SD_JOURNAL_RUNTIME_ONLY.html: man/sd_journal_open.html $(html-alias) @@ -1472,15 +1479,18 @@ man/sd_journal_has_persistent_files.html: man/sd_journal_has_runtime_files.html man/sd_journal_next_skip.html: man/sd_journal_next.html $(html-alias) -man/sd_journal_open_container.html: man/sd_journal_open.html +man/sd_journal_open_directory.html: man/sd_journal_open.html $(html-alias) -man/sd_journal_open_directory.html: man/sd_journal_open.html +man/sd_journal_open_directory_fd.html: man/sd_journal_open.html $(html-alias) man/sd_journal_open_files.html: man/sd_journal_open.html $(html-alias) +man/sd_journal_open_files_fd.html: man/sd_journal_open.html + $(html-alias) + man/sd_journal_perror.html: man/sd_journal_print.html $(html-alias) diff --git a/Makefile.am b/Makefile.am index 0f475c6d09..a05c7ce4db 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3021,6 +3021,8 @@ systemd_nspawn_SOURCES = \ src/nspawn/nspawn-setuid.h \ src/nspawn/nspawn-stub-pid1.c \ src/nspawn/nspawn-stub-pid1.h \ + src/nspawn/nspawn-patch-uid.c \ + src/nspawn/nspawn-patch-uid.h \ src/core/mount-setup.c \ src/core/mount-setup.h \ src/core/loopback-setup.c \ @@ -3048,6 +3050,17 @@ systemd_nspawn_LDADD += \ libfirewall.la endif +test_patch_uid_SOURCES = \ + src/nspawn/nspawn-patch-uid.c \ + src/nspawn/nspawn-patch-uid.h \ + src/nspawn/test-patch-uid.c + +test_patch_uid_LDADD = \ + libshared.la + +manual_tests += \ + test-patch-uid + # ------------------------------------------------------------------------------ systemd_run_SOURCES = \ src/run/run.c @@ -4929,7 +4942,9 @@ libmachine_core_la_SOURCES = \ src/machine/machine-dbus.c \ src/machine/machine-dbus.h \ src/machine/image-dbus.c \ - src/machine/image-dbus.h + src/machine/image-dbus.h \ + src/machine/operation.c \ + src/machine/operation.h libmachine_core_la_LIBADD = \ libshared.la @@ -5814,6 +5829,14 @@ EXTRA_DIST += \ test/TEST-07-ISSUE-1981/Makefile \ test/TEST-07-ISSUE-1981/test-segfault.sh \ test/TEST-07-ISSUE-1981/test.sh \ + test/TEST-08-ISSUE-2730/Makefile \ + test/TEST-08-ISSUE-2730/test.sh \ + test/TEST-09-ISSUE-2691/Makefile \ + test/TEST-09-ISSUE-2691/test.sh \ + test/TEST-10-ISSUE-2467/Makefile \ + test/TEST-10-ISSUE-2467/test.sh \ + test/TEST-11-ISSUE-3166/Makefile \ + test/TEST-11-ISSUE-3166/test.sh \ test/test-functions EXTRA_DIST += \ @@ -33,11 +33,11 @@ Janitorial Clean-ups: Features: +* journalctl: make sure -f ends when the container indicated by -M terminates + * rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the kernel doesn't support linkat() that replaces existing files, currently) -* journalctl -f --no-tail fails for Lennart - * check if DeviceAllow= should split first, resolve specifiers later * transient units: don't bother with actually setting unit properties, we @@ -74,11 +74,9 @@ Features: * PID1: find a way how we can reload unit file configuration for specific units only, without reloading the whole of systemd -* add an explicit parser for LimitNICE= and LimitRTPRIO= that verifies +* add an explicit parser for LimitRTPRIO= that verifies the specified range and generates sane error messages for incorrect - specifications. Also, for LimitNICE= maybe introduce a syntax such - as "+5" or "-7" in order to make the limits more readable as they - are otherwise shifted by 20. + specifications. * do something about "/control" subcgroups in the unified cgroup hierarchy @@ -99,9 +97,6 @@ Features: * install: include generator dirs in unit file search paths -* rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring - to unicode chars, to make things more expressive. - * fstab-generator: default to tmpfs-as-root if only usr= is specified on the kernel cmdline * docs: bring http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime up to date @@ -151,8 +146,6 @@ Features: * as soon as we have kdbus, and sender timestamps, revisit coalescing multiple parallel daemon reloads: http://lists.freedesktop.org/archives/systemd-devel/2014-December/025862.html -* the install state probably shouldn't get confused by generated units, think dbus1/kdbus compat! - * in systemctl list-unit-files: show the install value the presets would suggest for a service in a third column * figure out when we can use the coarse timers @@ -164,8 +157,6 @@ Features: * firstboot: make it useful to be run immediately after yum --installroot to set up a machine. (most specifically, make --copy-root-password work even if /etc/passwd already exists -* add infrastructure to allocate dynamic/transient users and UID ranges, for use in user-namespaced containers, per-seat gdm login screens and gdm guest sessions - * maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment= * introduce systemd-timesync-wait.service or so to sync on an NTP fix? @@ -252,7 +243,7 @@ Features: CAP_NET_ADMIN is set, more than the loopback device is defined, even when it is otherwise off -* MessageQueueMessageSize= and RLimitFSIZE= (and suchlike) should use parse_iec_size(). +* MessageQueueMessageSize= (and suchlike) should use parse_iec_size(). * "busctl status" works only as root on dbus1, since we cannot read /proc/$PID/exe @@ -267,7 +258,7 @@ Features: and passes this back to PID1 via SCM_RIGHTS. This also could be used to allow Chown/chgrp on sockets without requiring NSS in PID 1. -* New service property: maximum CPU and wallclock runtime for a service +* New service property: maximum CPU runtime for a service * introduce bus call FreezeUnit(s, b), as well as "systemctl freeze $UNIT" and "systemctl thaw $UNIT" as wrappers around this. The calls @@ -509,8 +500,6 @@ Features: written to as FAIL, but instead show that their are being written to. - add journalctl -H that talks via ssh to a remote peer and passes through binary logs data - - change journalctl -M to acquire fd to journal directory via machined, and - then operate on that via openat() instead of absolute paths - add a version of --merge which also merges /var/log/journal/remote - log accumulated resource usage after each service invocation - journalctl: -m should access container journals directly by enumerating @@ -549,7 +538,6 @@ Features: * unit install: - "systemctl mask" should find all names by which a unit is accessible (i.e. by scanning for symlinks to it) and link them all to /dev/null - - systemctl list-unit-files should list generated files (and probably with a new state "generated" for them, or so) * timer units: - timer units should get the ability to trigger when: @@ -596,8 +584,6 @@ Features: - should send out sd_notify("WATCHDOG=1") messages - optionally automatically add FORWARD rules to iptables whenever nspawn is running, remove them when shut down. - - add a logic for cleaning up read-only, hidden container images in - /var/lib/machines that are not ancestors of any non-hidden containers - Improve error message when --bind= is used on a non-existing source directory - maybe make copying of /etc/resolv.conf optional, and skip it if --read-only @@ -641,8 +627,6 @@ Features: * initialize the hostname from the fs label of /, if /etc/hostname does not exist? -* rename "userspace" to "core-os" - * udev: - move to LGPL - kill scsi_id @@ -757,11 +741,6 @@ Features: - Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely - consider adding RuntimeDirectoryUser= + RuntimeDirectoryGroup= -* systemd-python: - - figure out a simple way to wait for journal events in a way that - works with ^C - - add documentation to systemd.daemon - * udev-link-config: - Make sure ID_PATH is always exported and complete for network devices where possible, so we can safely rely diff --git a/catalog/systemd.be.catalog b/catalog/systemd.be.catalog index be081d6efc..051f49492f 100644 --- a/catalog/systemd.be.catalog +++ b/catalog/systemd.be.catalog @@ -53,7 +53,7 @@ Documentation: man:journald.conf(5) Паведамленні іншых сэрвісаў засталіся. Мяжа, пасля якой паведамленні будуць адкінуты, наладжваецца з -дапамогай RateLimitInterval= і RateLimitBurst= у файле +дапамогай RateLimitIntervalSec= і RateLimitBurst= у файле /etc/systemd/journald.conf. Глядзіце journald.conf(5) для дэталей. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.be@latin.catalog b/catalog/systemd.be@latin.catalog index 382fdb8b04..6ab361aafb 100644 --- a/catalog/systemd.be@latin.catalog +++ b/catalog/systemd.be@latin.catalog @@ -53,7 +53,7 @@ Majcie na ŭvazie, što byli adkinuty paviedamliennia toĺki hetaha servisu. Paviedamlienni inšych servisaŭ zastalisia. Miaža, paslia jakoj paviedamlienni buduć adkinuty, naladžvajecca z -dapamohaj RateLimitInterval= i RateLimitBurst= u fajlie +dapamohaj RateLimitIntervalSec= i RateLimitBurst= u fajlie /etc/systemd/journald.conf. Hliadzicie journald.conf(5) dlia detaliej. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.catalog b/catalog/systemd.catalog index 077f182a5a..90929bca6d 100644 --- a/catalog/systemd.catalog +++ b/catalog/systemd.catalog @@ -66,7 +66,7 @@ Note that only messages from the service in question have been dropped, other services' messages are unaffected. The limits controlling when messages are dropped may be configured -with RateLimitInterval= and RateLimitBurst= in +with RateLimitIntervalSec= and RateLimitBurst= in /etc/systemd/journald.conf. See journald.conf(5) for details. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.da.catalog b/catalog/systemd.da.catalog index bd4d742d8a..093e8139da 100644 --- a/catalog/systemd.da.catalog +++ b/catalog/systemd.da.catalog @@ -52,7 +52,7 @@ 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 +med RateLimitIntervalSec= og RateLimitBurst= i /etc/systemd/journald.conf. Se journald.conf(5) for detaljer herom. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.fr.catalog b/catalog/systemd.fr.catalog index 03a457786f..d71c2902d7 100644 --- a/catalog/systemd.fr.catalog +++ b/catalog/systemd.fr.catalog @@ -51,7 +51,7 @@ Notez que seuls des messages de ce service ont été évincés, les messages des autres services ne sont pas affectés. Les limites définissant ce comportement peuvent être configurées avec les -paramètres RateLimitInterval= et RateLimitBurst= dans le fichier +paramètres RateLimitIntervalSec= et RateLimitBurst= dans le fichier /etc/systemd/journald.conf. Voir journald.conf(5) pour plus de détails. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.hr.catalog b/catalog/systemd.hr.catalog new file mode 100644 index 0000000000..350988dd87 --- /dev/null +++ b/catalog/systemd.hr.catalog @@ -0,0 +1,314 @@ +# 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 +# Croatian translation + +# Format kataloga je dokumentiran na +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# Za pojašnjenje zašto ovo radimo, posjetite https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: journal je pokrenut +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Journal proces sustava se pokrenuo, otvorio je journal + datoteke za upis i spreman je za obradu zahtjeva. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: journal je zaustavljen +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Journal proces sustava je isključio i zatvorio sve trenutno +aktivne journal datoteke. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Diskovni prostor koji koristi journal +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +@JOURNAL_NAME@ (@JOURNAL_PATH@) trenutno koristi @CURRENT_USE_PRETTY@. +Najveća dopuštena upotreba je postavljena na @MAX_USE_PRETTY@. +Ostavljam najmanje @DISK_KEEP_FREE_PRETTY@ slobodno (trenutno dostupno @DISK_AVAILABLE_PRETTY@ diskovnog prostora). +Prisilno ograničenje upotrebe je @LIMIT_PRETTY@, od kojeg je @AVAILABLE_PRETTY@ još dostupno. + +Ograničenja kontroliraju koliko diskovnog prostora koristi journal mogu +se podesiti sa SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, +RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= settings u +/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Poruka iz usluge je potisnuta +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:journald.conf(5) + +Usluga je prijavila previše poruka u određenom vremenskom razdoblju. Poruke +iz usluge su odbačene. + +Zapamtite da samo poruke iz usluge u upitu su +odbačene, ostale poruke usluga nisu zahvaćene. + +Ograničenja koja kontroliraju kada je poruka odbačena mogu se podesiti +sa RateLimitIntervalSec= i RateLimitBurst= u +/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Journal poruka je propuštena +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Kernel poruka je izgubljena zato jer ih journal sustav nije mogao +dovoljno brzo obraditi. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Proces @COREDUMP_PID@ (@COREDUMP_COMM@) je izbacio jezgru +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:core(5) + +Proces @COREDUMP_PID@ (@COREDUMP_COMM@) se srušio i izbacio jezgru. + +Rušenje programa je uobičajeno uzrokovano greškom u programiranju i +trebalo bi se prijaviti razvijatelju kao greška. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Nova sesija @SESSION_ID@ je stvorena za korisnika @USER_ID@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Nova sesija sa ID @SESSION_ID@ je stvorena za korisnika @USER_ID@. + +Glavni proces sesije je @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Sesija @SESSION_ID@ je prekinuta +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Sesija sa ID @SESSION_ID@ je prekinuta. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Novo sjedište @SEAT_ID@ je sada dostupno +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Novo sjedište @SEAT_ID@ je podešeno i sada je dostupno. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Sjedište @SEAT_ID@ je sada uklonjeno +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Sjedište @SEAT_ID@ je uklonjeno i više nije dostupno. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Vrijeme promjene +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Sat sustava je promijenjen na @REALTIME@ microsekundi nakon 1. Siječnja, 1970 godine. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Vremenska zona je promijenjena u @TIMEZONE@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Vremenska zona je promijenjena u @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Pokretanje sustava je sada završeno +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Sve usluge sustava koje su zadane za pokretanje pri pokretanju sustava +su uspješno pokrenute. Zapamtite da ovo ne znači da sada računalo +miruje zato jer se neke usluge još uvijek mogu pokretati. + +Pokretanje kernela zahtijeva @KERNEL_USEC@ mikrosekundi. + +Pokretanje početnog RAM diska zahtijeva @INITRD_USEC@ mikrosekundi. + +Pokretanje prostora korisnika zahtijeva @USERSPACE_USEC@ mikrosekundi. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Pokrenuto je stanje spavanja @SLEEP@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Sustav je sada pokrenuo stanje spavanja @SLEEP@ + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Završeno je stanje spavanja @SLEEP@ +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Sustav je sada završio stanje spavanja @SLEEP@ + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Pokrenuto je isključivanje sustava +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Pokrenuto je isključivanje sustava. Isključivanje je sada pokrenuto, +sve usluge sustava su prekinute i svi datotečni sustavi su odmontirani. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Jedinica @UNIT@ je započela pokretanje +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je započela pokretanje. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Jedinica @UNIT@ je završila pokretanje +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je završila pokretanje. + +Rezultat pokretanja je @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Jedinica @UNIT@ je započela isključivanje +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je započela isključivanje. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Jedinica @UNIT@ je završila isključivanje +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je završila isključivanje. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Jedinica @UNIT@ nije uspjela +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ nije uspjela. + +Rezultat je @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je započela ponovno učitavati podešavanja + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Jedinica @UNIT@ je završila ponovno učitavati podešavanja +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedinica @UNIT@ je završila ponovno učitavati podešavanja + +Rezultat je @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Proces @EXECUTABLE@ se ne može pokrenuti +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Proces @EXECUTABLE@ se ne može pokrenuti i nije uspio. + +Broj greške vraćen ovim procesom je @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava, usluge +su pokrenute istovremeno s journalom. Ovo uobičajeno označava da +implementacija dnevnika sustava ne može slijediti brzinu +zahtjeva poruka. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Točka montiranja nije prazna +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Direktorij @WHERE@ je određen za točku montiranja (drugi redak u +/etc/fstab ili Where= redak u datoteci systemd jedinice) i nije prazan. +To ne utječe na montiranje, ali postojeće datoteke u ovom direktoriju +postaju nedostupne. Kako bi vidjeli datoteke preko kojih je montirano, +ručno montirajte osnovni datotečni sustav na drugu lokaciju. + +-- 24d8d4452573402496068381a6312df2 +Subject: Virtualni stroj ili spremnik su pokrenuti +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Virtualni stroj @NAME@ sa vodećim @LEADER@ PID-om je +pokrenut i spreman je za korištenje. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Virtualni stroj ili spremnik su isključeni +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel + +Virtualni stroj @NAME@ sa vodećim PID-om @LEADER@ je +isključen. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: DNSSEC način je isključen, jer ga poslužitelj ne podržava +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Usluga razrješavanja (systemd-resolved.service) je otkrila da +podešeni DNS poslužitelj ne podržava DNSSEC, i DNSSEC, kao rezultat +provjera je isključena. + +Ovaj događaj će zauzeti mjesto ako je DNSSEC=allow-downgrade podešen u +resolved.conf i podešeni DNS poslužitelj je nekompatibilan s DNSSEC. Zapamtite +da korištenje ovog načina dopušta povećanje DNSSEC napada, napadač bi mogao +isključiti DNSSEC provjeru na sustavu umetanjem DNS odgovora u +komunikacijski kanal što rezultira povećanjem napada poput ovog. + +Ovaj događaj bi mogao označavati da je DNS poslužitelj uistinu nekompatibilan s +DNSSEC ili da je napadač uspješno izvršio takav napad. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: DNSSEC provjera neuspješna +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:systemd-resolved.service(8) + +DNS zahtjev ili snimak resursa nije prošao DNSSEC provjeru. To uobičajeno +označava da je komunikacijski kanal mijenjan. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: DNSSEC pouzdano sidro je opozvano +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel +Documentation: man:systemd-resolved.service(8) + +A DNSSEC trust anchor has been revoked. A new trust anchor has to be +configured, or the operating system needs to be updated, to provide an updated +DNSSEC trust anchor. diff --git a/catalog/systemd.hu.catalog b/catalog/systemd.hu.catalog index 30d76916cc..68e8c2572e 100644 --- a/catalog/systemd.hu.catalog +++ b/catalog/systemd.hu.catalog @@ -51,7 +51,7 @@ Ne feledje, hogy csak a kérdéses szolgáltatás üzenetei kerültek eldobásra más szolgáltatások üzeneteit ez nem befolyásolja. Az üzenetek eldobását vezérlő korlátok az /etc/systemd/journald.conf -RateLimitInterval= és RateLimitBurst= beállításaival adhatók meg. +RateLimitIntervalSec= és RateLimitBurst= beállításaival adhatók meg. Részletekért lásd a journald.conf(5) man oldalt. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.it.catalog b/catalog/systemd.it.catalog index 861b92b74a..b6fca48221 100644 --- a/catalog/systemd.it.catalog +++ b/catalog/systemd.it.catalog @@ -46,7 +46,7 @@ Solo i messaggi del servizio indicato sono stati eliminati, i messaggi degli altri servizi rimangono invariati. I limiti oltre i quali i messaggi si eliminano si configurano -con RateLimitInterval= e RateLimitBurst= in +con RateLimitIntervalSec= e RateLimitBurst= in /etc/systemd/journald.conf. Vedi journald.conf(5) per maggiori informazioni. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.ko.catalog b/catalog/systemd.ko.catalog index 3c3535a94c..2fc6b60b1b 100644 --- a/catalog/systemd.ko.catalog +++ b/catalog/systemd.ko.catalog @@ -55,7 +55,7 @@ Documentation: man:journald.conf(5) 다른 서비스의 메시지에는 영향을 주지 않습니다. 메시지 거절 제어 제한 값은 /etc/systemd/journald.conf 의 -RateLimitInterval= 변수와 RateLimitBurst= 변수로 설정합니다. +RateLimitIntervalSec= 변수와 RateLimitBurst= 변수로 설정합니다. 자세한 내용은 ournald.conf(5)를 살펴보십시오. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.pl.catalog b/catalog/systemd.pl.catalog index 0d2e3d22cf..d8059e93cd 100644 --- a/catalog/systemd.pl.catalog +++ b/catalog/systemd.pl.catalog @@ -69,7 +69,7 @@ Proszę zauważyć, że tylko komunikaty z danej usługi zostały pominięte. Ni to wpływu na komunikaty innych usług. Ograniczenia kontrolujące pomijanie komunikatów mogą być konfigurowane -za pomocą opcji RateLimitInterval= i RateLimitBurst= w pliku +za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej informacji. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.pt_BR.catalog b/catalog/systemd.pt_BR.catalog index d9716e30f7..8b856e8355 100644 --- a/catalog/systemd.pt_BR.catalog +++ b/catalog/systemd.pt_BR.catalog @@ -53,7 +53,7 @@ Note que apenas mensagens de um serviço em questão foram descartadas; outras mensagens dos serviços não foram afetadas. Os controles de limites de quando as mensagens são descartadas pode ser -configurado com RateLimitInterval= e RateLimitBurst= no +configurado com RateLimitIntervalSec= e RateLimitBurst= no /etc/systemd/journald.conf. Veja journald.conf(5) para detalhes. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.ru.catalog b/catalog/systemd.ru.catalog index eedbb8aa9c..e56dbe3acc 100644 --- a/catalog/systemd.ru.catalog +++ b/catalog/systemd.ru.catalog @@ -76,7 +76,7 @@ Documentation: man:journald.conf(5) сообщения других служб не затронуты. Предел, после которого служба журнала начинает игнорировать сообщения, -настраивается параметрами RateLimitInterval= и RateLimitBurst= в файле +настраивается параметрами RateLimitIntervalSec= и RateLimitBurst= в файле /etc/systemd/journald.conf. Подробности смотрите на странице руководства journald.conf(5). diff --git a/catalog/systemd.sr.catalog b/catalog/systemd.sr.catalog index cf700c477b..cc689b7956 100644 --- a/catalog/systemd.sr.catalog +++ b/catalog/systemd.sr.catalog @@ -52,7 +52,7 @@ Documentation: man:journald.conf(5) услуге нису захваћене овим. Ограничења која подешавају начин на који се поруке одбацују се могу подесити -помоћу „RateLimitInterval=“ и „RateLimitBurst=“ параметара унутар датотеке +помоћу „RateLimitIntervalSec=“ и „RateLimitBurst=“ параметара унутар датотеке /etc/systemd/journald.conf. Погледајте journald.conf(5) за појединости. -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/catalog/systemd.zh_CN.catalog b/catalog/systemd.zh_CN.catalog index 38639109e4..ed59fc9250 100644 --- a/catalog/systemd.zh_CN.catalog +++ b/catalog/systemd.zh_CN.catalog @@ -50,7 +50,7 @@ Documentation: man:journald.conf(5) 请注意只有由有问题的服务传来的消息被丢弃, 其它服务的消息不受影响。 -可以在 /etc/systemd/journald.conf 中设定 RateLimitInterval= +可以在 /etc/systemd/journald.conf 中设定 RateLimitIntervalSec= 以及 RateLimitBurst = 的值以控制丢弃信息的限制。 请参见 journald.conf(5) 以了解详情。 diff --git a/catalog/systemd.zh_TW.catalog b/catalog/systemd.zh_TW.catalog index 027ffe44e5..aa5004db08 100644 --- a/catalog/systemd.zh_TW.catalog +++ b/catalog/systemd.zh_TW.catalog @@ -53,7 +53,7 @@ Documentation: man:journald.conf(5) 其他服務的訊息則不受影響。 可以在 /etc/systemd/journald.conf 中設定 -RateLimitInterval= 以及 RateLimitBurst= +RateLimitIntervalSec= 以及 RateLimitBurst= 來控制當訊息要開始被丟棄時的限制。參見 journald.conf(5) 以獲得更多資訊。 -- e9bf28e6e834481bb6f48f548ad13606 diff --git a/configure.ac b/configure.ac index d4e8ab6664..d6320d2022 100644 --- a/configure.ac +++ b/configure.ac @@ -336,7 +336,7 @@ AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE, IFLA_BRIDGE_VLAN_INFO, IFLA_BRPORT_PROXYARP, IFLA_BRPORT_LEARNING_SYNC, - IFLA_BR_PRIORITY, + IFLA_BR_VLAN_DEFAULT_PVID, NDA_IFINDEX, IFA_FLAGS], [], [], [[ @@ -507,6 +507,14 @@ if test "x$enable_apparmor" != "xno"; then fi AM_CONDITIONAL(HAVE_APPARMOR, [test "$have_apparmor" = "yes"]) +have_adm_group=no +AC_ARG_ENABLE(adm-group, AS_HELP_STRING([--disable-adm-group], [disable adm group])) +AS_IF([test "x$enable_adm_group" != "xno"], [ + AC_DEFINE(ENABLE_ADM_GROUP, 1, [Define if the ACL for adm group should be enabled]) + have_adm_group=yes + M4_DEFINES="$M4_DEFINES -DENABLE_ADM_GROUP" +]) + have_wheel_group=no AC_ARG_ENABLE(wheel-group, AS_HELP_STRING([--disable-wheel-group], [disable wheel group])) AS_IF([test "x$enable_wheel_group" != "xno"], [ @@ -1657,6 +1665,7 @@ AC_MSG_RESULT([ Zsh completions dir: ${with_zshcompletiondir} Extra start script: ${RC_LOCAL_SCRIPT_PATH_START} Extra stop script: ${RC_LOCAL_SCRIPT_PATH_STOP} + Adm group: ${have_adm_group} Wheel group: ${have_wheel_group} Debug shell: ${SUSHELL} @ ${DEBUGTTY} TTY GID: ${TTY_GID} diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index ffd06f040c..fd49b03493 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -496,6 +496,12 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr* KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute +# HP Folio 1040g2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr* + KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + # HP ProBook 6555b evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:* KEYBOARD_KEY_b2=www # Earth diff --git a/man/journald.conf.xml b/man/journald.conf.xml index a9690e8138..3964cd6bc5 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -148,12 +148,12 @@ </varlistentry> <varlistentry> - <term><varname>RateLimitInterval=</varname></term> + <term><varname>RateLimitIntervalSec=</varname></term> <term><varname>RateLimitBurst=</varname></term> <listitem><para>Configures the rate limiting that is applied to all messages generated on the system. If, in the time - interval defined by <varname>RateLimitInterval=</varname>, + interval defined by <varname>RateLimitIntervalSec=</varname>, more messages than specified in <varname>RateLimitBurst=</varname> are logged by a service, all further messages within the interval are dropped until the @@ -162,7 +162,7 @@ per-service, so that two services which log do not interfere with each other's limits. Defaults to 1000 messages in 30s. The time specification for - <varname>RateLimitInterval=</varname> may be specified in the + <varname>RateLimitIntervalSec=</varname> may be specified in the following units: <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>ms</literal>, <literal>us</literal>. To turn off any kind of rate limiting, diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 42d5e006bb..9c04849f66 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -322,6 +322,15 @@ </varlistentry> <varlistentry> + <term><varname>systemd.default_timeout_start_sec=</varname></term> + + <listitem> + <para>Overwrites the default start job timeout <varname>DefaultTimeoutStartSec=</varname> at boot. For details, + see <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>modules-load=</varname></term> <term><varname>rd.modules-load=</varname></term> diff --git a/man/machinectl.xml b/man/machinectl.xml index 43a3b98840..4b7f9a0391 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -589,13 +589,11 @@ <varlistentry> <term><command>clone</command> <replaceable>NAME</replaceable> <replaceable>NAME</replaceable></term> - <listitem><para>Clones a container or VM image. The - arguments specify the name of the image to clone and the name - of the newly cloned image. Note that plain directory container - images are cloned into subvolume images with this command. - Note that cloning a container or VM image is optimized for - btrfs file systems, and might not be efficient on others, due - to file system limitations.</para> + <listitem><para>Clones a container or VM image. The arguments specify the name of the image to clone and the + name of the newly cloned image. Note that plain directory container images are cloned into btrfs subvolume + images with this command, if the underlying file system supports this. Note that cloning a container or VM + image is optimized for btrfs file systems, and might not be efficient on others, due to file system + limitations.</para> <para>Note that this command leaves host name, machine ID and all other settings that could identify the instance diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml index fef453f8dc..153af2387f 100644 --- a/man/sd_journal_open.xml +++ b/man/sd_journal_open.xml @@ -45,14 +45,16 @@ <refnamediv> <refname>sd_journal_open</refname> <refname>sd_journal_open_directory</refname> + <refname>sd_journal_open_directory_fd</refname> <refname>sd_journal_open_files</refname> - <refname>sd_journal_open_container</refname> + <refname>sd_journal_open_files_fd</refname> <refname>sd_journal_close</refname> <refname>sd_journal</refname> <refname>SD_JOURNAL_LOCAL_ONLY</refname> <refname>SD_JOURNAL_RUNTIME_ONLY</refname> <refname>SD_JOURNAL_SYSTEM</refname> <refname>SD_JOURNAL_CURRENT_USER</refname> + <refname>SD_JOURNAL_OS_ROOT</refname> <refpurpose>Open the system journal for reading</refpurpose> </refnamediv> @@ -74,6 +76,13 @@ </funcprototype> <funcprototype> + <funcdef>int <function>sd_journal_open_directory_fd</function></funcdef> + <paramdef>sd_journal **<parameter>ret</parameter></paramdef> + <paramdef>int <parameter>fd</parameter></paramdef> + <paramdef>int <parameter>flags</parameter></paramdef> + </funcprototype> + + <funcprototype> <funcdef>int <function>sd_journal_open_files</function></funcdef> <paramdef>sd_journal **<parameter>ret</parameter></paramdef> <paramdef>const char **<parameter>paths</parameter></paramdef> @@ -81,9 +90,10 @@ </funcprototype> <funcprototype> - <funcdef>int <function>sd_journal_open_container</function></funcdef> + <funcdef>int <function>sd_journal_open_files_fd</function></funcdef> <paramdef>sd_journal **<parameter>ret</parameter></paramdef> - <paramdef>const char *<parameter>machine</parameter></paramdef> + <paramdef>int <parameter>fds[]</parameter></paramdef> + <paramdef>unsigned <parameter>n_fds</parameter></paramdef> <paramdef>int <parameter>flags</parameter></paramdef> </funcprototype> @@ -117,29 +127,28 @@ <constant>SD_JOURNAL_CURRENT_USER</constant> are specified, all journal file types will be opened.</para> - <para><function>sd_journal_open_directory()</function> is similar - to <function>sd_journal_open()</function> but takes an absolute - directory path as argument. All journal files in this directory - will be opened and interleaved automatically. This call also takes - a flags argument, but it must be passed as 0 as no flags are - currently understood for this call.</para> - - <para><function>sd_journal_open_files()</function> is similar to - <function>sd_journal_open()</function> but takes a - <constant>NULL</constant>-terminated list of file paths to open. - All files will be opened and interleaved automatically. This call - also takes a flags argument, but it must be passed as 0 as no - flags are currently understood for this call. Please note that in - the case of a live journal, this function is only useful for - debugging, because individual journal files can be rotated at any - moment, and the opening of specific files is inherently - racy.</para> - - <para><function>sd_journal_open_container()</function> is similar - to <function>sd_journal_open()</function> but opens the journal - files of a running OS container. The specified machine name refers - to a container that is registered with - <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> + <para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but + takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved + automatically. This call also takes a flags argument. The only flags parameter accepted by this call is + <constant>SD_JOURNAL_OS_ROOT</constant>. If specified, the journal files are searched below the usual + <filename>/var/log/journal</filename> and <filename>/run/log/journal</filename> relative to the specified path, + instead of directly beneath it.</para> + + <para><function>sd_journal_open_directory_fd()</function> is similar to + <function>sd_journal_open_directory()</function>, but takes a file descriptor referencing a directory in the file + system instead of an absolute file system path.</para> + + <para><function>sd_journal_open_files()</function> is similar to <function>sd_journal_open()</function> but takes a + <constant>NULL</constant>-terminated list of file paths to open. All files will be opened and interleaved + automatically. This call also takes a flags argument, but it must be passed as 0 as no flags are currently + understood for this call. Please note that in the case of a live journal, this function is only useful for + debugging, because individual journal files can be rotated at any moment, and the opening of specific files is + inherently racy.</para> + + <para><function>sd_journal_open_files_fd()</function> is similar to <function>sd_journal_open_files()</function> + but takes an array of open file descriptors that must reference journal files, instead of an array of file system + paths. Pass the array of file descriptors as second argument, and the number of array entries in the third. The + flags parameter must be passed as 0.</para> <para><varname>sd_journal</varname> objects cannot be used in the child after a fork. Functions which take a journal object as an @@ -205,26 +214,6 @@ </refsect1> <refsect1> - <title>History</title> - - <para><function>sd_journal_open()</function>, - <function>sd_journal_close()</function>, - <constant>SD_JOURNAL_LOCAL_ONLY</constant>, - <constant>SD_JOURNAL_RUNTIME_ONLY</constant>, - <constant>SD_JOURNAL_SYSTEM_ONLY</constant> were added in - systemd-38.</para> - - <para><function>sd_journal_open_directory()</function> was added - in systemd-187.</para> - - <para><constant>SD_JOURNAL_SYSTEM</constant>, - <constant>SD_JOURNAL_CURRENT_USER</constant>, and - <function>sd_journal_open_files()</function> were added in - systemd-205. <constant>SD_JOURNAL_SYSTEM_ONLY</constant> was - deprecated.</para> - </refsect1> - - <refsect1> <title>See Also</title> <para> diff --git a/man/sd_uid_get_state.xml b/man/sd_uid_get_state.xml index 4cc7405dd6..130af761da 100644 --- a/man/sd_uid_get_state.xml +++ b/man/sd_uid_get_state.xml @@ -218,19 +218,6 @@ </refsect1> <refsect1> - <title>History</title> - - <para><function>sd_uid_get_state()</function>, - <function>sd_uid_is_on_seat()</function>, - <function>sd_uid_get_sessions()</function>, and - <function>sd_uid_get_seats()</function> functions were added in - systemd-31.</para> - - <para><function>sd_uid_get_display()</function> was added in - systemd-213.</para> - </refsect1> - - <refsect1> <title>See Also</title> <para> diff --git a/man/sd_watchdog_enabled.xml b/man/sd_watchdog_enabled.xml index 6e27528a71..3de9899453 100644 --- a/man/sd_watchdog_enabled.xml +++ b/man/sd_watchdog_enabled.xml @@ -155,18 +155,6 @@ </refsect1> <refsect1> - <title>History</title> - - <para>The watchdog functionality and the - <varname>$WATCHDOG_USEC</varname> variable were added in - systemd-41.</para> - - <para><function>sd_watchdog_enabled()</function> function was - added in systemd-209. Since that version, the - <varname>$WATCHDOG_PID</varname> variable is also set.</para> - </refsect1> - - <refsect1> <title>See Also</title> <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index a0376ed3e0..bd688a0ee1 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -387,38 +387,79 @@ <varlistentry> <term><option>--private-users=</option></term> - <listitem><para>Enables user namespacing. If enabled, the - container will run with its own private set of Unix user and - group ids (UIDs and GIDs). Takes none, one or two - colon-separated parameters: the first parameter specifies the - first host UID to assign to the container, the second - parameter specifies the number of host UIDs to assign to the - container. If the second parameter is omitted, 65536 UIDs are - assigned. If the first parameter is also omitted (and hence - no parameter passed at all), the first UID assigned to the - container is read from the owner of the root directory of the - container's directory tree. By default, no user namespacing is - applied.</para> - - <para>Note that user namespacing currently requires OS trees - that are prepared for the UID shift that is being applied: - UIDs and GIDs used for file ownership or in file ACL entries - must be shifted to the container UID base that is - used during container runtime.</para> - - <para>It is recommended to assign at least 65536 UIDs to each - container, so that the usable UID range in the container - covers 16 bit. For best security, do not assign overlapping UID - ranges to multiple containers. It is hence a good idea to use - the upper 16 bit of the host 32-bit UIDs as container - identifier, while the lower 16 bit encode the container UID - used.</para> - - <para>When user namespaces are used, the GID range assigned to - each container is always chosen identical to the UID - range.</para></listitem> + <listitem><para>Controls user namespacing. If enabled, the container will run with its own private set of UNIX + user and group ids (UIDs and GIDs). This involves mapping the private UIDs/GIDs used in the container (starting + with the container's root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other + purposes (usually in the range beyond the host's UID/GID 65536). The parameter may be specified as follows:</para> + + <orderedlist> + <listitem><para>The value <literal>no</literal> turns off user namespacing. This is the default.</para></listitem> + + <listitem><para>The value <literal>yes</literal> (or the omission of a parameter) turns on user + namespacing. The UID/GID range to use is determined automatically from the file ownership of the root + directory of the container's directory tree. To use this option, make sure to prepare the directory tree in + advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd like to + use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate range. If this + mode is used the number of UIDs/GIDs assigned to the container for use is 65536, and the UID/GID of the + root directory must be a multiple of 65536.</para></listitem> + + <listitem><para>The value "pick" turns on user namespacing. In this case the UID/GID range is automatically + chosen. As first step, the file owner of the root directory of the container's directory tree is read, and it + is checked that it is currently not used by the system otherwise (in particular, that no other container is + using it). If this check is successful, the UID/GID range determined this way is used, similar to the + behaviour if "yes" is specified. If the check is not successful (and thus the UID/GID range indicated in the + root directory's file owner is already used elsewhere) a new – currently unused – UID/GID range of 65536 + UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and 1878982656, always starting at a + multiple of 65536. This setting implies <option>--private-users-chown</option> (see below), which has the + effect that the files and directories in the container's directory tree will be owned by the appropriate + users of the range picked. Using this option makes user namespace behaviour fully automatic. Note that the + first invocation of a previously unused container image might result in picking a new UID/GID range for it, + and thus in the (possibly expensive) file ownership adjustment operation. However, subsequent invocations of + the container will be cheap (unless of course the picked UID/GID range is assigned to a different use by + then).</para></listitem> + + <listitem><para>Finally if one or two colon-separated numeric parameters are specified, user namespacing is + turned on, too. The first parameter specifies the first host UID/GID to assign to the container, the second + parameter specifies the number of host UIDs/GIDs to assign to the container. If the second parameter is + omitted, 65536 UIDs/GIDs are assigned.</para></listitem> + </orderedlist> + + <para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the + container covers 16 bit. For best security, do not assign overlapping UID/GID ranges to multiple containers. It is + hence a good idea to use the upper 16 bit of the host 32-bit UIDs/GIDs as container identifier, while the lower 16 + bit encode the container UID/GID used. This is in fact the behaviour enforced by the + <option>--private-users=pick</option> option.</para> + + <para>When user namespaces are used, the GID range assigned to each container is always chosen identical to the + UID range.</para> + + <para>In most cases, using <option>--private-users=pick</option> is the recommended option as it enhances + container security massively and operates fully automatically in most cases.</para> + + <para>Note that the picked UID/GID range is not written to <filename>/etc/passwd</filename> or + <filename>/etc/group</filename>. In fact, the allocation of the range is not stored persistently anywhere, + except in the file ownership of the files and directories of the container.</para></listitem> </varlistentry> + <varlistentry> + <term><option>-U</option></term> + + <listitem><para>If the kernel supports the user namespaces feature, equivalent to + <option>--private-users=pick</option>, otherwise equivalent to + <option>--private-users=no</option>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--private-users-chown</option></term> + + <listitem><para>If specified, all files and directories in the container's directory tree will adjusted so that + they are owned to the appropriate UIDs/GIDs selected for the container (see above). This operation is + potentially expensive, as it involves descending and iterating through the full directory tree of the + container. Besides actual file ownership, file ACLs are adjusted as well.</para> + + <para>This option is implied if <option>--private-users=pick</option> is used. This option has no effect if + user namespacing is not used.</para></listitem> + </varlistentry> <varlistentry> <term><option>--private-network</option></term> diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index edc6df914a..8833e73c72 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -271,16 +271,16 @@ </varlistentry> <varlistentry> - <term><varname>DefaultStartLimitInterval=</varname></term> + <term><varname>DefaultStartLimitIntervalSec=</varname></term> <term><varname>DefaultStartLimitBurst=</varname></term> <listitem><para>Configure the default unit start rate limiting, as configured per-service by - <varname>StartLimitInterval=</varname> and + <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname>. See <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details on the per-service settings. - <varname>DefaultStartLimitInterval=</varname> defaults to + <varname>DefaultStartLimitIntervalSec=</varname> defaults to 10s. <varname>DefaultStartLimitBurst=</varname> defaults to 5.</para></listitem> </varlistentry> diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index 93bae2a6dc..a43dc981bd 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -66,14 +66,13 @@ [Install] sections. The automount specific configuration options are configured in the [Automount] section.</para> - <para>Automount units must be named after the automount - directories they control. Example: the automount point - <filename noindex='true'>/home/lennart</filename> must be - configured in a unit file - <filename>home-lennart.automount</filename>. For details about the - escaping logic used to convert a file system path to a unit name - see - <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + <para>Automount units must be named after the automount directories they control. Example: the automount point + <filename noindex='true'>/home/lennart</filename> must be configured in a unit file + <filename>home-lennart.automount</filename>. For details about the escaping logic used to convert a file system + path to a unit name see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Note that + automount units cannot be templated, nor is it possible to add multiple names to an automount unit by creating + additional symlinks to its unit file.</para> <para>For each automount unit file a matching mount unit file (see <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 4ed62dbada..2a93760428 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -94,11 +94,9 @@ required to access <filename>/tmp</filename> and <filename>/var/tmp</filename>.</para> - <para>Units whose output standard output or error output is - connected to any other sink but <option>null</option>, - <option>tty</option> and <option>socket</option> automatically - acquire dependencies of type <varname>After=</varname> on - <filename>journald.socket</filename>.</para> + <para>Units whose standard output or error output is connected to <option>journal</option>, <option>syslog</option> + or <option>kmsg</option> (or their combinations with console output, see below) automatically acquire dependencies + of type <varname>After=</varname> on <filename>systemd-journald.socket</filename>.</para> </refsect1> <refsect1> @@ -470,6 +468,10 @@ similar to the same option of <varname>StandardInput=</varname>.</para> + <para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the + kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on + <filename>systemd-journald.socket</filename> (also see the automatic dependencies section above).</para> + <para>This setting defaults to the value set with <option>DefaultStandardOutput=</option> in <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, @@ -627,27 +629,23 @@ <term><varname>LimitNICE=</varname></term> <term><varname>LimitRTPRIO=</varname></term> <term><varname>LimitRTTIME=</varname></term> - <listitem><para>These settings set both soft and hard limits - of various resources for executed processes. See - <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> - for details. The resource limit is possible to specify in two formats, - <option>value</option> to set soft and hard limits to the same value, - or <option>soft:hard</option> to set both limits individually (e.g. LimitAS=4G:16G). - Use the string <varname>infinity</varname> to - configure no limit on a specific resource. The multiplicative - suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E - may be used for resource limits measured in bytes - (e.g. LimitAS=16G). For the limits referring to time values, - the usual time units ms, s, min, h and so on may be used (see - <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> - for details). Note that if no time unit is specified for - <varname>LimitCPU=</varname> the default unit of seconds is - implied, while for <varname>LimitRTTIME=</varname> the default - unit of microseconds is implied. Also, note that the effective - granularity of the limits might influence their - enforcement. For example, time limits specified for - <varname>LimitCPU=</varname> will be rounded up implicitly to - multiples of 1s.</para> + <listitem><para>Set soft and hard limits on various resources for executed processes. See + <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details on + the resource limit concept. Resource limits may be specified in two formats: either as single value to set a + specific soft and hard limit to the same value, or as colon-separated pair <option>soft:hard</option> to set + both limits individually (e.g. <literal>LimitAS=4G:16G</literal>). Use the string <varname>infinity</varname> + to configure no limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base + 1024) may be used for resource limits measured in bytes (e.g. LimitAS=16G). For the limits referring to time + values, the usual time units ms, s, min, h and so on may be used (see + <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for + details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of seconds + is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is implied. Also, note + that the effective granularity of the limits might influence their enforcement. For example, time limits + specified for <varname>LimitCPU=</varname> will be rounded up implicitly to multiples of 1s. For + <varname>LimitNICE=</varname> the value may be specified in two syntaxes: if prefixed with <literal>+</literal> + or <literal>-</literal>, the value is understood as regular Linux nice value in the range -20..19. If not + prefixed like this the value is understood as raw resource limit parameter in the range 0..40 (with 0 being + equivalent to 1).</para> <para>Note that most process resource limits configured with these options are per-process, and processes may fork in order diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index f116782b40..bf56a49e58 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -82,14 +82,12 @@ will refuse options that are not listed in <filename>/etc/fstab</filename> if it is not run as UID 0.</para> - <para>Mount units must be named after the mount point directories - they control. Example: the mount point - <filename noindex='true'>/home/lennart</filename> must be - configured in a unit file <filename>home-lennart.mount</filename>. - For details about the escaping logic used to convert a file system - path to a unit name, see - <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. - Note that mount units cannot be templated.</para> + <para>Mount units must be named after the mount point directories they control. Example: the mount point <filename + noindex='true'>/home/lennart</filename> must be configured in a unit file <filename>home-lennart.mount</filename>. + For details about the escaping logic used to convert a file system path to a unit name, see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Note that mount + units cannot be templated, nor is possible to add multiple names to a mount unit by creating additional symlinks to + it.</para> <para>Optionally, a mount unit may be accompanied by an automount unit, to allow on-demand or parallelized mounting. See diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 9bf1b198ad..2a20748376 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -815,9 +815,8 @@ <varlistentry> <term><varname>ClientIdentifier=</varname></term> <listitem> - <para>DHCP client identifier to use. Either <literal>mac</literal> - to use the MAC address of the link or <literal>duid</literal> - (the default) to use a RFC4361-compliant Client ID.</para> + <para>The DHCPv4 client identifier to use. Either <literal>mac</literal> to use the MAC address of the link + or <literal>duid</literal> (the default, see below) to use a RFC4361-compliant Client ID.</para> </listitem> </varlistentry> <varlistentry> @@ -828,6 +827,25 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>DUIDRawData=</varname></term> + <listitem><para>Specifies the DHCP DUID bytes as a single newline-terminated, hexadecimal string, with each + byte separated by a ':'. A DHCPv6 client sends the DHCP Unique Identifier (DUID) and the interface Identity + Association Identifier (IAID) to a DHCP server when acquiring a dynamic IPv6 address. Similar, DHCPv4 clients + send the IAID and DUID to the DHCP server when acquiring a dynamic IPv4 address if + <option>ClientIdentifier=duid</option>. IAID and DUID allows a DHCP server to uniquely identify the machine + and the interface requesting a DHCP IP address.</para> + + <para>The DUID value specified here takes precedence over the DUID that systemd-networkd generates + using the machine-id from the <filename>/etc/machine-id</filename> file, as well as the + global DUID that may be specified in <citerefentry><refentrytitle>networkd.conf + </refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + + <para>The configured DHCP DUID should conform to the specification in + <ulink url="http://tools.ietf.org/html/rfc3315#section-9">RFC 3315</ulink>, + <ulink url="http://tools.ietf.org/html/rfc6355">RFC 6355</ulink>.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>RequestBroadcast=</varname></term> <listitem> <para>Request the server to use broadcast messages before @@ -846,40 +864,7 @@ </listitem> </varlistentry> </variablelist> - - </refsect1> - - <refsect1> - <title>[DUID] Section Options</title> - - <para>This section configures the DHCP Unique Identifier (DUID) value used by DHCP - protocol. DHCPv6 client protocol sends the DHCP Unique Identifier and the interface - Identity Association Identifier (IAID) to a DHCP server when acquiring a dynamic IPv6 - address. DHCPv4 client protocol sends IAID and DUID to the DHCP server when acquiring - a dynamic IPv4 address if <option>ClientIdentifier=duid</option>. IAID and DUID allows a - DHCP server to uniquely identify the machine and the interface requesting a DHCP IP.</para> - - <para>The DUID value specified here overrides the DUID that systemd-networkd generates - using the machine-id from the <filename>/etc/machine-id</filename> file, as well as the - global DUID that may be specified in <citerefentry><refentrytitle>networkd.conf - </refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> - - <para>The configured DHCP DUID should conform to the specification in - <ulink url="http://tools.ietf.org/html/rfc3315#section-9">RFC 3315</ulink>, - <ulink url="http://tools.ietf.org/html/rfc6355">RFC 6355</ulink>.</para> - - <para>The following options are available in <literal>[DUID]</literal> section:</para> - - <variablelist class='network-directives'> - - <varlistentry> - <term><varname>RawData=</varname></term> - <listitem><para>Specifies the DUID bytes as a single newline-terminated, hexadecimal - string, with each byte separated by a ':'.</para></listitem> - </varlistentry> - - </variablelist> - </refsect1> + </refsect1> <refsect1> <title>[DHCPServer] Section Options</title> diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index ce900a5db1..15360078ef 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -251,6 +251,14 @@ <option>--uuid=</option> command line switch. This option is privileged (see above). </para></listitem> </varlistentry> + + <varlistentry> + <term><varname>PrivateUsers=</varname></term> + + <listitem><para>Configures support for usernamespacing. This is equivalent to the + <option>--private-users=</option> command line switch, and takes the same options. This option is privileged + (see above). </para></listitem> + </varlistentry> </variablelist> </refsect1> @@ -314,6 +322,16 @@ for details about the specific options supported. This setting is privileged (see above).</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>PrivateUsersChown=</varname></term> + + <listitem><para>Configures whether the ownership of the files and directories in the container tree shall be + adjusted to the UID/GID range used, if necessary and user namespacing is enabled. This is equivalent to the + <option>--private-users-chown</option> command line switch. This option is privileged (see + above). </para></listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 16247628af..eee98d99ee 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -71,6 +71,9 @@ the root slice <filename>-.slice</filename>. </para> + <para>Note that slice units cannot be templated, nor is possible to add multiple names to a slice unit by creating + additional symlinks to it.</para> + <para>By default, service and scope units are placed in <filename>system.slice</filename>, virtual machines and containers registered with diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 2d6339680b..735268c79d 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -807,6 +807,22 @@ suffix.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>TriggerLimitIntervalSec=</varname></term> + <term><varname>TriggerLimitBurst=</varname></term> + + <listitem><para>Configures a limit on how often this socket unit my be activated within a specific time + interval. The <varname>TriggerLimitIntervalSec=</varname> may be used to configure the length of the time + interval in the usual time units <literal>us</literal>, <literal>ms</literal>, <literal>s</literal>, + <literal>min</literal>, <literal>h</literal>, … and defaults to 5s (See + <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details on + the various time units available). The <varname>TriggerLimitBurst=</varname> setting takes an integer value and + specifies the numer of permitted activations per time interval, and defaults to 2500 (thus by default + permitting 2500 activations per 5s). Set either to 0 to disable any form of trigger rate limiting. If the limit + is hit, the socket unit is placed into a failure mode, and will not be connectible anymore until + restarted. Note that this limit is enforced before the service activation is enqueued.</para></listitem> + </varlistentry> + </variablelist> <para>Check diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index 6fc4c7bf13..cf4e1ba839 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -78,13 +78,12 @@ which configure resource control settings for these processes of the unit.</para> - <para>Swap units must be named after the devices - or files they control. Example: the swap device - <filename noindex='true'>/dev/sda5</filename> must be configured in a - unit file <filename>dev-sda5.swap</filename>. For details about - the escaping logic used to convert a file system path to a unit - name, see - <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + <para>Swap units must be named after the devices or files they control. Example: the swap device <filename + noindex='true'>/dev/sda5</filename> must be configured in a unit file <filename>dev-sda5.swap</filename>. For + details about the escaping logic used to convert a file system path to a unit name, see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Note that swap + units cannot be templated, nor is possible to add multiple names to a swap unit by creating additional symlinks to + it.</para> </refsect1> <refsect1> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index eb0afee141..0d0cccf152 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -73,6 +73,12 @@ <filename>foo.timer</filename> activates a matching service <filename>foo.service</filename>. The unit to activate may be controlled by <varname>Unit=</varname> (see below).</para> + + <para>Note that in case the unit to activate is already active at the time the timer elapses it is not restarted, + but simply left running. There is no concept of spawning new service instances in this case. Due to this, services + with <varname>RemainAfterExit=</varname> set (which stay around continously even after the service's main process + exited) are usually not suitable for activation via repetitive timers, as they will only be activated once, and + then stay around forever.</para> </refsect1> <refsect1> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 7993301167..abd47bd237 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -751,14 +751,14 @@ </varlistentry> <varlistentry> - <term><varname>StartLimitInterval=</varname></term> + <term><varname>StartLimitIntervalSec=</varname></term> <term><varname>StartLimitBurst=</varname></term> <listitem><para>Configure unit start rate limiting. By default, units which are started more than 5 times within 10 seconds are not permitted to start any more times until the 10 second interval ends. With these two - options, this rate limiting may be modified. Use <varname>StartLimitInterval=</varname> to configure the - checking interval (defaults to <varname>DefaultStartLimitInterval=</varname> in manager configuration file, set - to 0 to disable any kind of rate limiting). Use <varname>StartLimitBurst=</varname> to configure how many + options, this rate limiting may be modified. Use <varname>StartLimitIntervalSec=</varname> to configure the + checking interval (defaults to <varname>DefaultStartLimitIntervalSec=</varname> in manager configuration file, + set to 0 to disable any kind of rate limiting). Use <varname>StartLimitBurst=</varname> to configure how many starts per interval are allowed (defaults to <varname>DefaultStartLimitBurst=</varname> in manager configuration file). These configuration options are particularly useful in conjunction with the service setting <varname>Restart=</varname> (see @@ -769,14 +769,15 @@ manually at a later point, from which point on, the restart logic is again activated. Note that <command>systemctl reset-failed</command> will cause the restart rate counter for a service to be flushed, which is useful if the administrator wants to manually start a unit and the start limit interferes with - that.</para></listitem> + that. Note that this rate-limiting is enforced after any unit condition checks are executed, and hence unit + activations with failing conditions are not counted by this rate limiting.</para></listitem> </varlistentry> <varlistentry> <term><varname>StartLimitAction=</varname></term> <listitem><para>Configure the action to take if the rate limit configured with - <varname>StartLimitInterval=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of + <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>, <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or <option>poweroff-immediate</option>. If <option>none</option> is set, hitting the rate limit will trigger no @@ -1102,13 +1103,12 @@ <varlistentry> <term><varname>Alias=</varname></term> - <listitem><para>A space-separated list of additional names - this unit shall be installed under. The names listed here must - have the same suffix (i.e. type) as the unit file name. This - option may be specified more than once, in which case all - listed names are used. At installation time, - <command>systemctl enable</command> will create symlinks from - these names to the unit filename.</para></listitem> + <listitem><para>A space-separated list of additional names this unit shall be installed under. The names listed + here must have the same suffix (i.e. type) as the unit file name. This option may be specified more than once, + in which case all listed names are used. At installation time, <command>systemctl enable</command> will create + symlinks from these names to the unit filename. Note that not all unit types support such alias names, and this + setting is not supported for them. Specifically, mount, slice, swap, and automount units do not support + aliasing.</para></listitem> </varlistentry> <varlistentry> diff --git a/po/LINGUAS b/po/LINGUAS index 2774a3228f..0916707be0 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -2,6 +2,7 @@ de el fr gl +hr hu it ko diff --git a/po/hr.po b/po/hr.po new file mode 100644 index 0000000000..a0aff4331f --- /dev/null +++ b/po/hr.po @@ -0,0 +1,570 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# gogo <trebelnik2@gmail.com>, 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: systemd master\n" +"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" +"POT-Creation-Date: 2016-27-04 11:57+0100\n" +"PO-Revision-Date: 2016-04-27 12:11+0200\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7.1\n" +"Last-Translator: gogo <trebelnik2@gmail.com>com>\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Language: hr\n" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1 +msgid "Send passphrase back to system" +msgstr "Pošalji lozinku natrag u sustav" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2 +msgid "" +"Authentication is required to send the entered passphrase back to the system." +msgstr "Potrebna je ovjera za slanje upisane lozinke natrag u sustav." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3 +msgid "Manage system services or other units" +msgstr "Upravljajte uslugama sustava ili drugim jedinicama" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4 +msgid "Authentication is required to manage system services or other units." +msgstr "Potrebna je ovjera za upravljanje uslugama sustava ili jedinicama." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5 +msgid "Manage system service or unit files" +msgstr "Upravljajte uslugama sustava ili datotekama jedinica" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6 +msgid "Authentication is required to manage system service or unit files." +msgstr "" +"Potrebna je ovjera za upravljanje uslugama sustava ili datotekama jedinica." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7 +msgid "Set or unset system and service manager environment variables" +msgstr "Postavite ili uklonite varijable okruženja sustava i usluga" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8 +msgid "" +"Authentication is required to set or unset system and service manager " +"environment variables." +msgstr "" +"Potrebna je ovjera za postavljanje ili uklanjanje varijabla okruženja " +"sustava i usluga." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9 +msgid "Reload the systemd state" +msgstr "Ponovno učitaj systemd stanje" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10 +msgid "Authentication is required to reload the systemd state." +msgstr "Potrebna je ovjera za ponovno učitavanje systemd stanja." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 +msgid "Set host name" +msgstr "Postavi naziv računala" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 +msgid "Authentication is required to set the local host name." +msgstr "Potrebna je ovjera za postavljanje naziva lokalnog računala." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 +msgid "Set static host name" +msgstr "Postavi nepromjenjivi naziv račumala" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 +msgid "" +"Authentication is required to set the statically configured local host name, " +"as well as the pretty host name." +msgstr "" +"Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, " +"kao i prijatnog naziva računala." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5 +msgid "Set machine information" +msgstr "Postavi informacije računala" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6 +msgid "Authentication is required to set local machine information." +msgstr "Potrebna je ovjera za postavljanje informacije lokalnog računala." + +#: ../src/import/org.freedesktop.import1.policy.in.h:1 +msgid "Import a VM or container image" +msgstr "Uvezi VM ili spremnik slike" + +#: ../src/import/org.freedesktop.import1.policy.in.h:2 +msgid "Authentication is required to import a VM or container image" +msgstr "Potrebna je ovjera za uvoz WM ili spremnika slike" + +#: ../src/import/org.freedesktop.import1.policy.in.h:3 +msgid "Export a VM or container image" +msgstr "Izvezi VM ili spremnik slike" + +#: ../src/import/org.freedesktop.import1.policy.in.h:4 +msgid "Authentication is required to export a VM or container image" +msgstr "Potrebna je ovjera za izvoz WM ili spremnika slike" + +#: ../src/import/org.freedesktop.import1.policy.in.h:5 +msgid "Download a VM or container image" +msgstr "Preuzmi VM ili spremnik slike" + +#: ../src/import/org.freedesktop.import1.policy.in.h:6 +msgid "Authentication is required to download a VM or container image" +msgstr "Potrebna je ovjera za preuzimanje VM ili spremnika slike." + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:1 +msgid "Set system locale" +msgstr "Postavi sustav lokalizacije" + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:2 +msgid "Authentication is required to set the system locale." +msgstr "Potrebna je ovjera za postavljanje sustava lokalizacije." + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:3 +msgid "Set system keyboard settings" +msgstr "Postavi postavke tipkovnice sustava" + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:4 +msgid "Authentication is required to set the system keyboard settings." +msgstr "Potrebna je ovjera za postavljanje postavki tipkovnice sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:1 +msgid "Allow applications to inhibit system shutdown" +msgstr "Dopusti aplikacijama zaustavljanje isključivanja sustava" + +#: ../src/login/org.freedesktop.login1.policy.in.h:2 +msgid "" +"Authentication is required for an application to inhibit system shutdown." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje isključivanja " +"sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:3 +msgid "Allow applications to delay system shutdown" +msgstr "Dopusti aplikacijama odgodu isključivanja sustava" + +#: ../src/login/org.freedesktop.login1.policy.in.h:4 +msgid "Authentication is required for an application to delay system shutdown." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama odgode isključivanja sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:5 +msgid "Allow applications to inhibit system sleep" +msgstr "Dopusti aplikacijama zaustavljanje spavanja sustava" + +#: ../src/login/org.freedesktop.login1.policy.in.h:6 +msgid "Authentication is required for an application to inhibit system sleep." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanja spavanja sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:7 +msgid "Allow applications to delay system sleep" +msgstr "Dopusti aplikacijama odgodu spavanja sustava" + +#: ../src/login/org.freedesktop.login1.policy.in.h:8 +msgid "Authentication is required for an application to delay system sleep." +msgstr "Potrebna je ovjera za odgodu spavanja sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:9 +msgid "Allow applications to inhibit automatic system suspend" +msgstr "Dopusti aplikacijama zaustavljanje automatskog suspendiranja sustava" + +#: ../src/login/org.freedesktop.login1.policy.in.h:10 +msgid "" +"Authentication is required for an application to inhibit automatic system " +"suspend." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje automatskog " +"suspendiranja sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:11 +msgid "Allow applications to inhibit system handling of the power key" +msgstr "" +"Dopusti aplikacijama sprječavanje rukovanja sustava tipkom isključivanja" + +#: ../src/login/org.freedesktop.login1.policy.in.h:12 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the power key." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava " +"tipkom isključivanja." + +#: ../src/login/org.freedesktop.login1.policy.in.h:13 +msgid "Allow applications to inhibit system handling of the suspend key" +msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom suspenzije" + +#: ../src/login/org.freedesktop.login1.policy.in.h:14 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the suspend key." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava " +"tipkom suspenzije." + +#: ../src/login/org.freedesktop.login1.policy.in.h:15 +msgid "Allow applications to inhibit system handling of the hibernate key" +msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom hibernacije" + +#: ../src/login/org.freedesktop.login1.policy.in.h:16 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the hibernate key." +msgstr "" +"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava " +"tipkom hibernacije." + +#: ../src/login/org.freedesktop.login1.policy.in.h:17 +msgid "Allow applications to inhibit system handling of the lid switch" +msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava preklopnicama" + +#: ../src/login/org.freedesktop.login1.policy.in.h:18 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the lid switch." +msgstr "" +"Potrebna je ovjera za dopuštenje sprječavanja rukovanja sustava " +"preklopnicama." + +#: ../src/login/org.freedesktop.login1.policy.in.h:19 +msgid "Allow non-logged-in users to run programs" +msgstr "Dopusti neprijavljenim korisnicima pokretanje programa" + +#: ../src/login/org.freedesktop.login1.policy.in.h:20 +msgid "Authentication is required to run programs as a non-logged-in user." +msgstr "" +"Potrebna je ovjera za dopuštenje neprijavljenim korisnicima pokretanje " +"programa." + +#: ../src/login/org.freedesktop.login1.policy.in.h:21 +msgid "Allow attaching devices to seats" +msgstr "Dopusti povezivanje uređaja skupu sesija i hardvera" + +#: ../src/login/org.freedesktop.login1.policy.in.h:22 +msgid "Authentication is required for attaching a device to a seat." +msgstr "Potrebna je ovjera za povezivanje uređaja sa skupom sesija i hardvera." + +#: ../src/login/org.freedesktop.login1.policy.in.h:23 +msgid "Flush device to seat attachments" +msgstr "Ukloni povezani uređaj sa skupa sesija i hardvera" + +#: ../src/login/org.freedesktop.login1.policy.in.h:24 +msgid "" +"Authentication is required for resetting how devices are attached to seats." +msgstr "" +"Potrebna je ovjera za obnovu povezivanja uređaja sa skupom sesija i hardvera." + +#: ../src/login/org.freedesktop.login1.policy.in.h:25 +msgid "Power off the system" +msgstr "Isključi sustav" + +#: ../src/login/org.freedesktop.login1.policy.in.h:26 +msgid "Authentication is required for powering off the system." +msgstr "Potrebna je ovjera za isključivanje sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:27 +msgid "Power off the system while other users are logged in" +msgstr "Isključi sustav kada su ostali korisnici prijavljeni" + +#: ../src/login/org.freedesktop.login1.policy.in.h:28 +msgid "" +"Authentication is required for powering off the system while other users are " +"logged in." +msgstr "" +"Potrebna je ovjera za isključivanje sustava kada su ostali korisnici " +"prijavljeni." + +#: ../src/login/org.freedesktop.login1.policy.in.h:29 +msgid "Power off the system while an application asked to inhibit it" +msgstr "" +"Isključi sustav kada je aplikacija zatražila zaustavljanje isključivanja" + +#: ../src/login/org.freedesktop.login1.policy.in.h:30 +msgid "" +"Authentication is required for powering off the system while an application " +"asked to inhibit it." +msgstr "" +"Potrebna je ovjera za isključivanje sustava kada je aplikacija zatražila " +"zaustavljanje isključivanja." + +#: ../src/login/org.freedesktop.login1.policy.in.h:31 +msgid "Reboot the system" +msgstr "Ponovno pokreni sustav" + +#: ../src/login/org.freedesktop.login1.policy.in.h:32 +msgid "Authentication is required for rebooting the system." +msgstr "Potrebna je ovjera za ponovno pokretanje sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:33 +msgid "Reboot the system while other users are logged in" +msgstr "Ponovno pokreni sustav kada su ostali korisnici prijavljeni" + +#: ../src/login/org.freedesktop.login1.policy.in.h:34 +msgid "" +"Authentication is required for rebooting the system while other users are " +"logged in." +msgstr "" +"Potrebna je ovjera za ponovno pokretanje sustava kada su ostali korisnici " +"prijavljeni." + +#: ../src/login/org.freedesktop.login1.policy.in.h:35 +msgid "Reboot the system while an application asked to inhibit it" +msgstr "" +"Ponovno pokreni sustav kada je aplikacija zatražila zaustavljanje ponovnog " +"pokretanja" + +#: ../src/login/org.freedesktop.login1.policy.in.h:36 +msgid "" +"Authentication is required for rebooting the system while an application " +"asked to inhibit it." +msgstr "" +"Potrebna je ovjera za ponovno pokretanje sustava kada je aplikacija " +"zatražila zaustavljanje ponovnog pokretanja." + +#: ../src/login/org.freedesktop.login1.policy.in.h:37 +msgid "Suspend the system" +msgstr "Suspendiraj sustav" + +#: ../src/login/org.freedesktop.login1.policy.in.h:38 +msgid "Authentication is required for suspending the system." +msgstr "Potrebna je ovjera za suspendiranje sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:39 +msgid "Suspend the system while other users are logged in" +msgstr "Suspendiraj sustav kada su drugi korisnici prijavljeni" + +#: ../src/login/org.freedesktop.login1.policy.in.h:40 +msgid "" +"Authentication is required for suspending the system while other users are " +"logged in." +msgstr "" +"Potrebna je ovjera za suspendiranje sustava kada su drugi korisnici " +"prijavljeni." + +#: ../src/login/org.freedesktop.login1.policy.in.h:41 +msgid "Suspend the system while an application asked to inhibit it" +msgstr "" +"Suspendiraj sustav kada je aplikacija zatražila zaustavljanje suspendiranja" + +#: ../src/login/org.freedesktop.login1.policy.in.h:42 +msgid "" +"Authentication is required for suspending the system while an application " +"asked to inhibit it." +msgstr "" +"Potrebna je ovjera za suspendiranje sustava kada je aplikacija zatražila " +"zaustavljanje suspendiranja." + +#: ../src/login/org.freedesktop.login1.policy.in.h:43 +msgid "Hibernate the system" +msgstr "Hiberniraj sustav" + +#: ../src/login/org.freedesktop.login1.policy.in.h:44 +msgid "Authentication is required for hibernating the system." +msgstr "Potrebna je ovjera za hibernaciju sustava." + +#: ../src/login/org.freedesktop.login1.policy.in.h:45 +msgid "Hibernate the system while other users are logged in" +msgstr "Hiberniraj sustav kada su ostali korisnici prijavljeni." + +#: ../src/login/org.freedesktop.login1.policy.in.h:46 +msgid "" +"Authentication is required for hibernating the system while other users are " +"logged in." +msgstr "" +"Potrebna je ovjera za hibernaciju sustava kada su drugi korisnici " +"prijavljeni." + +#: ../src/login/org.freedesktop.login1.policy.in.h:47 +msgid "Hibernate the system while an application asked to inhibit it" +msgstr "" +"Hiberniraj sustav kada je aplikacija zatražila zaustavljanje hibernacije" + +#: ../src/login/org.freedesktop.login1.policy.in.h:48 +msgid "" +"Authentication is required for hibernating the system while an application " +"asked to inhibit it." +msgstr "" +"Potrebna je ovjera za hibernaciju sustava kada je aplikacija zatražila " +"zaustavljanje hibernacije." + +#: ../src/login/org.freedesktop.login1.policy.in.h:49 +msgid "Manage active sessions, users and seats" +msgstr "" +"Upravljanje aktivnim sesijama, korisnicima i skupovima sesija i hardvera" + +#: ../src/login/org.freedesktop.login1.policy.in.h:50 +msgid "" +"Authentication is required for managing active sessions, users and seats." +msgstr "" +"Potrebna je ovjera za upravljanje aktivnim sesijama, korisnicima i skupovima " +"sesija i hardvera." + +#: ../src/login/org.freedesktop.login1.policy.in.h:51 +msgid "Lock or unlock active sessions" +msgstr "Zaključavanje ili otključavanje aktivne sesije" + +#: ../src/login/org.freedesktop.login1.policy.in.h:52 +msgid "Authentication is required to lock or unlock active sessions." +msgstr "Potrebna je ovjera za zaključavanje ili otključavanje aktivne sesije." + +#: ../src/login/org.freedesktop.login1.policy.in.h:53 +msgid "Allow indication to the firmware to boot to setup interface" +msgstr "Dopusti najavu frimveru za pokretanje sučelja postavljanja" + +#: ../src/login/org.freedesktop.login1.policy.in.h:54 +msgid "" +"Authentication is required to indicate to the firmware to boot to setup " +"interface." +msgstr "Potrebna je ovjera najave frimvera za pokretanje sučelja postavljanja." + +#: ../src/login/org.freedesktop.login1.policy.in.h:55 +msgid "Set a wall message" +msgstr "Postavljanje zaslonske pruke" + +#: ../src/login/org.freedesktop.login1.policy.in.h:56 +msgid "Authentication is required to set a wall message" +msgstr "Potrebna je ovjera za postavljanje zaslonske pruke." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:1 +msgid "Log into a local container" +msgstr "Prijavi se u lokalni spremnik" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:2 +msgid "Authentication is required to log into a local container." +msgstr "Potrebna je ovjera za prijavu u lokalni spremnik." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:3 +msgid "Log into the local host" +msgstr "Prijava na lokalno računalo" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:4 +msgid "Authentication is required to log into the local host." +msgstr "Potrebna je ovjera za prijavu na lokalno račuanlo." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:5 +msgid "Acquire a shell in a local container" +msgstr "Pokretanje ljuske u lokalnom spremniku" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:6 +msgid "Authentication is required to acquire a shell in a local container." +msgstr "Potrebna je ovjera za pokretanje ljuske u lokalnom spremniku." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:7 +msgid "Acquire a shell on the local host" +msgstr "Pokretanje ljuske na lokalnom računalu" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:8 +msgid "Authentication is required to acquire a shell on the local host." +msgstr "Potrebna je ovjera za pokretanje ljuske na lokalnom računalu." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:9 +msgid "Acquire a pseudo TTY in a local container" +msgstr "Pokretanje pseudo TTY na lokalnom spremniku" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:10 +msgid "" +"Authentication is required to acquire a pseudo TTY in a local container." +msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom spremniku." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:11 +msgid "Acquire a pseudo TTY on the local host" +msgstr "Pokretanje pseudo TTY na lokalnom računalu" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:12 +msgid "Authentication is required to acquire a pseudo TTY on the local host." +msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom računalu." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:13 +msgid "Manage local virtual machines and containers" +msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:14 +msgid "" +"Authentication is required to manage local virtual machines and containers." +msgstr "" +"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i " +"spremnicima." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:15 +msgid "Manage local virtual machine and container images" +msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima slika" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:16 +msgid "" +"Authentication is required to manage local virtual machine and container " +"images." +msgstr "" +"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i " +"spremnicima slika." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1 +msgid "Set system time" +msgstr "Postavi vrijeme sustava" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2 +msgid "Authentication is required to set the system time." +msgstr "Potrebna je ovjera za postavljanje vremena sustava." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3 +msgid "Set system timezone" +msgstr "Postavi vremensku zonu sustava" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4 +msgid "Authentication is required to set the system timezone." +msgstr "Potrebna je ovjera za postavljanje vremenske zone sustava." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5 +msgid "Set RTC to local timezone or UTC" +msgstr "Postavi RTC u lokalnu vremensku zonu ili UTC" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6 +msgid "" +"Authentication is required to control whether the RTC stores the local or " +"UTC time." +msgstr "" +"Potrebna je ovjera za postavljanje RTC-a u lokalnu vremensku zonu ili UTC." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7 +msgid "Turn network time synchronization on or off" +msgstr "Uključi ili isključi mrežno uklađivanje vremena" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8 +msgid "" +"Authentication is required to control whether network time synchronization " +"shall be enabled." +msgstr "" +"Potrebna je ovjera za uključivanje ili isključivanje mrežnog usklađivanja " +"vremena." + +#: ../src/core/dbus-unit.c:428 +msgid "Authentication is required to start '$(unit)'." +msgstr "Potrebna je ovjera za pokretanje '$(unit)'." + +#: ../src/core/dbus-unit.c:429 +msgid "Authentication is required to stop '$(unit)'." +msgstr "Potrebna je ovjera za zaustavljanje '$(unit)'." + +#: ../src/core/dbus-unit.c:430 +msgid "Authentication is required to reload '$(unit)'." +msgstr "Potrebna je ovjera za ponovno učitavnje '$(unit)'." + +#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432 +msgid "Authentication is required to restart '$(unit)'." +msgstr "Potrebna je ovjera za ponovno pokretanje'$(unit)'." + +#: ../src/core/dbus-unit.c:535 +msgid "Authentication is required to kill '$(unit)'." +msgstr "Potrebna je ovjera za ubijanje '$(unit)'." + +#: ../src/core/dbus-unit.c:565 +msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." +msgstr "Potrebna je ovjera za vraćanje \"neuspjelog\" stanja '$(unit)'." + +#: ../src/core/dbus-unit.c:597 +msgid "Authentication is required to set properties on '$(unit)'." +msgstr "Potrebna je ovjera za postavljanje svojstava na '$(unit)'." diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules index 0b14bb4a11..7ad8a557ff 100644 --- a/rules/60-persistent-storage.rules +++ b/rules/60-persistent-storage.rules @@ -14,6 +14,10 @@ TEST=="whole_disk", GOTO="persistent_storage_end" # for partitions import parent information ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" +# NVMe +KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}" +KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n" + # virtio-blk KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" diff --git a/src/basic/architecture.c b/src/basic/architecture.c index a9ecfc1cd6..8e2c2b02d2 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -121,6 +121,8 @@ int uname_architecture(void) { { "tilegx", ARCHITECTURE_TILEGX }, #elif defined(__cris__) { "crisv32", ARCHITECTURE_CRIS }, +#elif defined(__nios2__) + { "nios2", ARCHITECTURE_NIOS2 }, #else #error "Please register your architecture here!" #endif @@ -171,6 +173,7 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_M68K] = "m68k", [ARCHITECTURE_TILEGX] = "tilegx", [ARCHITECTURE_CRIS] = "cris", + [ARCHITECTURE_NIOS2] = "nios2", }; DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h index c22cbc8279..91ec108e04 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -57,6 +57,7 @@ enum { ARCHITECTURE_M68K, ARCHITECTURE_TILEGX, ARCHITECTURE_CRIS, + ARCHITECTURE_NIOS2, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 }; @@ -187,6 +188,9 @@ int uname_architecture(void); #elif defined(__cris__) # define native_architecture() ARCHITECTURE_CRIS # error "Missing LIB_ARCH_TUPLE for CRIS" +#elif defined(__nios2__) +# define native_architecture() ARCHITECTURE_NIOS2 +# define LIB_ARCH_TUPLE "nios2-linux-gnu" #else # error "Please register your architecture here!" #endif diff --git a/src/basic/copy.c b/src/basic/copy.c index e2db4be9ff..c3586728d0 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -102,7 +102,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { if (try_cfr) { n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); if (n < 0) { - if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV)) + if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) return n; try_cfr = false; @@ -305,6 +305,8 @@ static int fd_copy_directory( fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); else fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); + if (fdf < 0) + return -errno; d = fdopendir(fdf); if (!d) @@ -325,22 +327,6 @@ static int fd_copy_directory( r = 0; - if (created) { - struct timespec ut[2] = { - st->st_atim, - st->st_mtim - }; - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - (void) futimens(fdt, ut); - (void) copy_xattr(dirfd(d), fdt); - } - FOREACH_DIRENT_ALL(de, d, return -errno) { struct stat buf; int q; @@ -364,7 +350,7 @@ static int fd_copy_directory( q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISFIFO(buf.st_mode)) q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) + else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); else q = -EOPNOTSUPP; @@ -376,6 +362,22 @@ static int fd_copy_directory( r = q; } + if (created) { + struct timespec ut[2] = { + st->st_atim, + st->st_mtim + }; + + if (fchown(fdt, st->st_uid, st->st_gid) < 0) + r = -errno; + + if (fchmod(fdt, st->st_mode & 07777) < 0) + r = -errno; + + (void) copy_xattr(dirfd(d), fdt); + (void) futimens(fdt, ut); + } + return r; } @@ -396,7 +398,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) return fd_copy_symlink(fdf, from, &st, fdt, to); else if (S_ISFIFO(st.st_mode)) return fd_copy_fifo(fdf, from, &st, fdt, to); - else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) + else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) return fd_copy_node(fdf, from, &st, fdt, to); else return -EOPNOTSUPP; @@ -407,7 +409,6 @@ int copy_tree(const char *from, const char *to, bool merge) { } int copy_directory_fd(int dirfd, const char *to, bool merge) { - struct stat st; assert(dirfd >= 0); @@ -422,6 +423,21 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) { return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); } +int copy_directory(const char *from, const char *to, bool merge) { + struct stat st; + + assert(from); + assert(to); + + if (lstat(from, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); +} + int copy_file_fd(const char *from, int fdt, bool try_reflink) { _cleanup_close_ int fdf = -1; int r; diff --git a/src/basic/copy.h b/src/basic/copy.h index 3e5eb52506..b5d08ebafe 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -30,6 +30,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace int copy_tree(const char *from, const char *to, bool merge); int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); int copy_directory_fd(int dirfd, const char *to, bool merge); +int copy_directory(const char *from, const char *to, bool merge); int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); int copy_times(int fdf, int fdt); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index 5fb535cb13..59067121b7 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -52,12 +52,10 @@ int dirent_ensure_type(DIR *d, struct dirent *de) { bool dirent_is_file(const struct dirent *de) { assert(de); - if (hidden_file(de->d_name)) + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) + if (hidden_or_backup_file(de->d_name)) return false; return true; @@ -66,12 +64,10 @@ bool dirent_is_file(const struct dirent *de) { bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { assert(de); - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; - if (hidden_file_allow_backup(de->d_name)) + if (de->d_name[0] == '.') return false; return endswith(de->d_name, suffix); diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 6bf099b46c..b91d04908f 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -38,7 +38,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu on_error; \ } \ break; \ - } else if (hidden_file((de)->d_name)) \ + } else if (hidden_or_backup_file((de)->d_name)) \ continue; \ else diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index ec9560cd07..8b466cff15 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -25,11 +25,13 @@ #include <unistd.h> #include "fd-util.h" +#include "fs-util.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" +#include "stdio-util.h" #include "util.h" int close_nointr(int fd) { @@ -229,7 +231,7 @@ int close_all_fds(const int except[], unsigned n_except) { while ((de = readdir(d))) { int fd = -1; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; if (safe_atoi(de->d_name, &fd) < 0) @@ -356,3 +358,17 @@ bool fdname_is_valid(const char *s) { return p - s < 256; } + +int fd_get_path(int fd, char **ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + int r; + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + r = readlink_malloc(procfs_path, ret); + + if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ + return -EBADF; + + return r; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 44528c6e35..b86e41698a 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -72,6 +72,8 @@ void cmsg_close_all(struct msghdr *mh); bool fdname_is_valid(const char *s); +int fd_get_path(int fd, char **ret); + /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) diff --git a/src/basic/fdset.c b/src/basic/fdset.c index 06f8ecbdbc..527f27bc67 100644 --- a/src/basic/fdset.c +++ b/src/basic/fdset.c @@ -151,7 +151,7 @@ int fdset_new_fill(FDSet **_s) { while ((de = readdir(d))) { int fd = -1; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; r = safe_atoi(de->d_name, &fd); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 51268828af..e24e7036f7 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -38,6 +38,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -493,3 +494,17 @@ int get_files_in_directory(const char *path, char ***list) { return n; } + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + xsprintf(path, "/proc/self/fd/%i", what); + + r = inotify_add_watch(fd, path, mask); + if (r < 0) + return -errno; + + return r; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 0d23f8635f..517b599d6f 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -72,3 +72,5 @@ union inotify_event_buffer { struct inotify_event ev; uint8_t raw[INOTIFY_EVENT_MAX]; }; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 85b8d812b3..49a0479592 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1773,20 +1773,18 @@ int set_consume(Set *s, void *value) { int set_put_strdup(Set *s, const char *p) { char *c; - int r; assert(s); assert(p); + if (set_contains(s, (char*) p)) + return 0; + c = strdup(p); if (!c) return -ENOMEM; - r = set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; + return set_consume(s, c); } int set_put_strdupv(Set *s, char **l) { diff --git a/src/basic/missing.h b/src/basic/missing.h index 6616f0b720..22ea8f67cc 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -445,6 +445,10 @@ struct btrfs_ioctl_quota_ctl_args { #define TMPFS_MAGIC 0x01021994 #endif +#ifndef MQUEUE_MAGIC +#define MQUEUE_MAGIC 0x19800202 +#endif + #ifndef MS_MOVE #define MS_MOVE 8192 #endif @@ -553,7 +557,7 @@ struct btrfs_ioctl_quota_ctl_args { #define IFLA_INET6_ADDR_GEN_MODE 8 #define __IFLA_INET6_MAX 9 -#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) #define IN6_ADDR_GEN_MODE_EUI64 0 #define IN6_ADDR_GEN_MODE_NONE 1 @@ -738,7 +742,7 @@ struct btrfs_ioctl_quota_ctl_args { #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BR_PRIORITY +#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID #define IFLA_BR_UNSPEC 0 #define IFLA_BR_FORWARD_DELAY 1 #define IFLA_BR_HELLO_TIME 2 diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 5faa2eba05..ba698959b7 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -498,7 +498,9 @@ bool fstype_is_network(const char *fstype) { "nfs4\0" "gfs\0" "gfs2\0" - "glusterfs\0"; + "glusterfs\0" + "pvfs2\0" /* OrangeFS */ + ; const char *x; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index c407263e16..7dc579a159 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -93,7 +93,7 @@ static inline int safe_atoli(const char *s, long int *ret_u) { #if SIZE_MAX == UINT_MAX static inline int safe_atozu(const char *s, size_t *ret_u) { assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, ret_u); + return safe_atou(s, (unsigned *) ret_u); } #else static inline int safe_atozu(const char *s, size_t *ret_u) { diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 044a12889d..b2fa81a294 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -756,34 +756,53 @@ char *file_in_same_dir(const char *path, const char *filename) { return ret; } -bool hidden_file_allow_backup(const char *filename) { - assert(filename); - - return - filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, ".rpmnew") || - endswith(filename, ".rpmsave") || - endswith(filename, ".rpmorig") || - endswith(filename, ".dpkg-old") || - endswith(filename, ".dpkg-new") || - endswith(filename, ".dpkg-tmp") || - endswith(filename, ".dpkg-dist") || - endswith(filename, ".dpkg-bak") || - endswith(filename, ".dpkg-backup") || - endswith(filename, ".dpkg-remove") || - endswith(filename, ".swp"); -} +bool hidden_or_backup_file(const char *filename) { + const char *p; -bool hidden_file(const char *filename) { assert(filename); - if (endswith(filename, "~")) + if (filename[0] == '.' || + streq(filename, "lost+found") || + streq(filename, "aquota.user") || + streq(filename, "aquota.group") || + endswith(filename, "~")) return true; - return hidden_file_allow_backup(filename); + p = strrchr(filename, '.'); + if (!p) + return false; + + /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up + * with always new suffixes and that everybody else should just adjust to that, then it really should be on + * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt + * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional + * string. Specifically: there's now: + * + * The generic suffixes "~" and ".bak" for backup files + * The generic prefix "." for hidden files + * + * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" + * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. + */ + + return STR_IN_SET(p + 1, + "rpmnew", + "rpmsave", + "rpmorig", + "dpkg-old", + "dpkg-new", + "dpkg-tmp", + "dpkg-dist", + "dpkg-bak", + "dpkg-backup", + "dpkg-remove", + "ucf-new", + "ucf-old", + "ucf-dist", + "swp", + "bak", + "old", + "new"); } bool is_device_path(const char *path) { diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 34d5cd1570..a27c13fcc3 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -122,7 +122,6 @@ bool path_is_safe(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); -bool hidden_file_allow_backup(const char *filename); -bool hidden_file(const char *filename) _pure_; +bool hidden_or_backup_file(const char *filename) _pure_; bool is_device_path(const char *path); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index f2cea01979..4a7367cc92 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -528,14 +528,20 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod return -EPROTO; } -void sigkill_wait(pid_t *pid) { +void sigkill_wait(pid_t pid) { + assert(pid > 1); + + if (kill(pid, SIGKILL) > 0) + (void) wait_for_terminate(pid, NULL); +} + +void sigkill_waitp(pid_t *pid) { if (!pid) return; if (*pid <= 1) return; - if (kill(*pid, SIGKILL) > 0) - (void) wait_for_terminate(*pid, NULL); + sigkill_wait(*pid); } int kill_and_sigcont(pid_t pid, int sig) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index ffd4bcb0ff..9f75088796 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -58,8 +58,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); -void sigkill_wait(pid_t *pid); -#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait) +void sigkill_wait(pid_t pid); +void sigkill_waitp(pid_t *pid); int kill_and_sigcont(pid_t pid, int sig); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index 7540b43215..ee063720ed 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -153,6 +153,56 @@ static int rlimit_parse_usec(const char *val, rlim_t *ret) { return 0; } +static int rlimit_parse_nice(const char *val, rlim_t *ret) { + uint64_t rl; + int r; + + /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the + * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is + * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight + * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we + * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0. + * + * Yeah, Linux is quality engineering sometimes... */ + + if (val[0] == '+') { + + /* Prefixed with "+": Parse as positive user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl >= PRIO_MAX) + return -ERANGE; + + rl = 20 - rl; + + } else if (val[0] == '-') { + + /* Prefixed with "-": Parse as negative user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (-PRIO_MIN)) + return -ERANGE; + + rl = 20 + rl; + } else { + + /* Not prefixed: parse as raw resource limit value */ + r = safe_atou64(val, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (20 - PRIO_MIN)) + return -ERANGE; + } + + *ret = (rlim_t) rl; + return 0; +} + static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = { [RLIMIT_CPU] = rlimit_parse_sec, [RLIMIT_FSIZE] = rlimit_parse_size, @@ -167,7 +217,7 @@ static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret [RLIMIT_LOCKS] = rlimit_parse_u64, [RLIMIT_SIGPENDING] = rlimit_parse_u64, [RLIMIT_MSGQUEUE] = rlimit_parse_size, - [RLIMIT_NICE] = rlimit_parse_u64, + [RLIMIT_NICE] = rlimit_parse_nice, [RLIMIT_RTPRIO] = rlimit_parse_u64, [RLIMIT_RTTIME] = rlimit_parse_usec, }; diff --git a/src/basic/user-util.h b/src/basic/user-util.h index c23f1d485d..8026eca3f4 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -21,6 +21,7 @@ #include <stdbool.h> #include <sys/types.h> +#include <unistd.h> bool uid_is_valid(uid_t uid); @@ -63,3 +64,7 @@ int take_etc_passwd_lock(const char *root); #define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) #define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + +static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; +} diff --git a/src/basic/util.c b/src/basic/util.c index b70c50047f..756c663be4 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -522,7 +522,7 @@ int on_ac_power(void) { if (!de) break; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 9c0e82ebb3..14eb46c8db 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -362,7 +362,7 @@ static int refresh_one( Group **ret) { _cleanup_closedir_ DIR *d = NULL; - Group *ours; + Group *ours = NULL; int r; assert(controller); diff --git a/src/core/automount.c b/src/core/automount.c index 7c55d7bc49..1239a0efc6 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1050,9 +1050,6 @@ const UnitVTable automount_vtable = { "Automount\0" "Install\0", - .no_alias = true, - .no_instances = true, - .init = automount_init, .load = automount_load, .done = automount_done, diff --git a/src/core/busname.c b/src/core/busname.c index f4f433340c..e7b7b5c012 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -1028,9 +1028,6 @@ const UnitVTable busname_vtable = { "Install\0", .private_section = "BusName", - .no_alias = true, - .no_instances = true, - .init = busname_init, .done = busname_done, .load = busname_load, diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index d2eb388f7c..d45f511489 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -889,7 +889,7 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e return sd_bus_reply_method_return(message, NULL); } -static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) { +static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; const char *k; @@ -929,6 +929,10 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e !strv_contains(states, unit_sub_state_to_string(u))) continue; + if (!strv_isempty(patterns) && + !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) + continue; + unit_path = unit_dbus_path(u); if (!unit_path) return -ENOMEM; @@ -963,7 +967,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e } static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return list_units_filtered(message, userdata, error, NULL); + return list_units_filtered(message, userdata, error, NULL, NULL); } static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -974,7 +978,23 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s if (r < 0) return r; - return list_units_filtered(message, userdata, error, states); + return list_units_filtered(message, userdata, error, states, NULL); +} + +static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_units_filtered(message, userdata, error, states, patterns); } static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1465,7 +1485,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } -static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; UnitFileList *item; @@ -1490,7 +1510,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu if (!h) return -ENOMEM; - r = unit_file_get_list(m->unit_file_scope, NULL, h); + r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns); if (r < 0) goto fail; @@ -1518,6 +1538,26 @@ fail: return r; } +static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return list_unit_files_by_patterns(message, userdata, error, NULL, NULL); +} + +static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_unit_files_by_patterns(message, userdata, error, states, patterns); +} + static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const char *name; @@ -2010,7 +2050,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2073,6 +2114,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2091,6 +2133,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index d33e494f6b..bb09a515f8 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -149,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index abe30413c3..e912fe2192 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -704,7 +704,8 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/device.c b/src/core/device.c index d01bec53d8..16e56efcc3 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -841,8 +841,6 @@ const UnitVTable device_vtable = { "Device\0" "Install\0", - .no_instances = true, - .init = device_init, .done = device_done, .load = unit_load_fragment_and_dropin_optional, diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5568b4696f..928b913c7b 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -164,6 +164,8 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LE Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout) Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) +Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) +m4_dnl The following is a legacy alias name for compatibility Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) @@ -220,6 +222,7 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0, Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) +m4_dnl The following three only exist for compatibility, they moved into Unit, see above Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) @@ -297,6 +300,8 @@ Socket.RemoveOnStop, config_parse_bool, 0, Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks) Socket.FileDescriptorName, config_parse_fdname, 0, 0 Socket.Service, config_parse_socket_service, 0, 0 +Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) +Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c4566f7709..1a8c03904c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3427,10 +3427,10 @@ int config_parse_protect_system( #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { + char *id = NULL; unsigned c = 0; int fd, r; FILE *f; - char *id = NULL; assert(filename); assert(*filename); @@ -3452,7 +3452,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { * the names of this unit, but only if it is a valid * unit name. */ name = basename(*filename); - if (unit_name_is_valid(name, UNIT_NAME_ANY)) { id = set_get(names, name); @@ -3492,6 +3491,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { *_f = f; *_final = id; + return 0; } @@ -3552,13 +3552,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { } static int load_from_path(Unit *u, const char *path) { - int r; _cleanup_set_free_free_ Set *symlink_names = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *filename = NULL; char *id = NULL; Unit *merged; struct stat st; + int r; assert(u); assert(path); @@ -3597,18 +3597,14 @@ static int load_from_path(Unit *u, const char *path) { r = -ENOENT; else r = open_follow(&filename, &f, symlink_names, &id); + if (r >= 0) + break; + filename = mfree(filename); + if (r != -ENOENT) + return r; - if (r < 0) { - filename = mfree(filename); - if (r != -ENOENT) - return r; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - continue; - } - - break; + /* Empty the symlink names for the next run */ + set_clear_free(symlink_names); } } @@ -3616,6 +3612,11 @@ static int load_from_path(Unit *u, const char *path) { /* Hmm, no suitable file found? */ return 0; + if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { + log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); + return -ELOOP; + } + merged = u; r = merge_by_names(&merged, symlink_names, id); if (r < 0) diff --git a/src/core/main.c b/src/core/main.c index 8dfb3928de..ed4d42c8cc 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -289,6 +289,7 @@ static int parse_crash_chvt(const char *value) { } static int set_machine_id(const char *m) { + assert(m); if (sd_id128_from_string(m, &arg_machine_id) < 0) return -EINVAL; @@ -413,6 +414,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { target = runlevel_to_target(key); if (target) return free_and_strdup(&arg_default_unit, target); + + } else if (streq(key, "systemd.default_timeout_start_sec") && value) { + + r = parse_sec(value, &arg_default_timeout_start_usec); + if (r < 0) + log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); + + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; } return 0; @@ -660,7 +670,8 @@ static int parse_config_file(void) { { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, - { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, + { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ + { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit }, diff --git a/src/core/mount.c b/src/core/mount.c index 188fb0aa40..c8a898e4dc 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -86,6 +86,15 @@ static bool mount_is_network(const MountParameters *p) { return mount_needs_network(p->options, p->fstype); } +static bool mount_is_loop(const MountParameters *p) { + assert(p); + + if (fstab_test_option(p->options, "loop\0")) + return true; + + return false; +} + static bool mount_is_bind(const MountParameters *p) { assert(p); @@ -269,12 +278,12 @@ static int mount_add_mount_links(Mount *m) { } /* Adds in links to other mount points that might be needed - * for the source path (if this is a bind mount) to be + * for the source path (if this is a bind mount or a loop mount) to be * available. */ pm = get_mount_parameters_fragment(m); if (pm && pm->what && path_is_absolute(pm->what) && - !mount_is_network(pm)) { + (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) { r = unit_require_mounts_for(UNIT(m), pm->what); if (r < 0) @@ -1830,9 +1839,6 @@ const UnitVTable mount_vtable = { "Install\0", .private_section = "Mount", - .no_alias = true, - .no_instances = true, - .init = mount_init, .load = mount_load, .done = mount_done, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index b732501364..6c504a5e69 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -70,10 +70,18 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitsByPatterns"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListUnitFiles"/> <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitFilesByPatterns"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="GetUnitFileState"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/scope.c b/src/core/scope.c index 7078d1f7e9..238f63a729 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -569,8 +569,6 @@ const UnitVTable scope_vtable = { "Install\0", .private_section = "Scope", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = scope_init, diff --git a/src/core/service.c b/src/core/service.c index b46dd8bcdd..f7a3fcf2b9 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -180,20 +180,17 @@ static int service_set_main_pid(Service *s, pid_t pid) { return 0; } -static void service_close_socket_fd(Service *s) { +void service_close_socket_fd(Service *s) { assert(s); - s->socket_fd = asynchronous_close(s->socket_fd); -} - -static void service_connection_unref(Service *s) { - assert(s); + /* Undo the effect of service_set_socket_fd(). */ - if (!UNIT_ISSET(s->accept_socket)) - return; + s->socket_fd = asynchronous_close(s->socket_fd); - socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); - unit_ref_unset(&s->accept_socket); + if (UNIT_ISSET(s->accept_socket)) { + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); + } } static void service_stop_watchdog(Service *s) { @@ -321,7 +318,6 @@ static void service_done(Unit *u) { s->bus_name_owner = mfree(s->bus_name_owner); service_close_socket_fd(s); - service_connection_unref(s); unit_ref_unset(&s->accept_socket); @@ -910,10 +906,8 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && - !(state == SERVICE_DEAD && UNIT(s)->job)) { + !(state == SERVICE_DEAD && UNIT(s)->job)) service_close_socket_fd(s); - service_connection_unref(s); - } if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) service_stop_watchdog(s); @@ -3139,9 +3133,8 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context assert(s); assert(fd >= 0); - /* This is called by the socket code when instantiating a new - * service for a stream socket and the socket needs to be - * configured. */ + /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs + * to be configured. We take ownership of the passed fd on success. */ if (UNIT(s)->load_state != UNIT_LOADED) return -EINVAL; @@ -3169,12 +3162,15 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context return r; } + r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); + if (r < 0) + return r; + s->socket_fd = fd; s->socket_fd_selinux_context_net = selinux_context_net; unit_ref_set(&s->accept_socket, UNIT(sock)); - - return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); + return 0; } static void service_reset_failed(Unit *u) { diff --git a/src/core/service.h b/src/core/service.h index a5ced215e4..c7f1e81bdb 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -80,7 +80,7 @@ typedef enum NotifyState { typedef enum ServiceResult { SERVICE_SUCCESS, - SERVICE_FAILURE_RESOURCES, + SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ SERVICE_FAILURE_TIMEOUT, SERVICE_FAILURE_EXIT_CODE, SERVICE_FAILURE_SIGNAL, @@ -198,6 +198,7 @@ struct Service { extern const UnitVTable service_vtable; int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net); +void service_close_socket_fd(Service *s); const char* service_restart_to_string(ServiceRestart i) _const_; ServiceRestart service_restart_from_string(const char *s) _pure_; diff --git a/src/core/slice.c b/src/core/slice.c index 63a77c9bca..c7700b8857 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -309,8 +309,6 @@ const UnitVTable slice_vtable = { "Install\0", .private_section = "Slice", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = slice_init, diff --git a/src/core/socket.c b/src/core/socket.c index a9fff9c259..7eeed068bd 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -99,6 +99,8 @@ static void socket_init(Unit *u) { s->exec_context.std_error = u->manager->default_std_error; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + + RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500); } static void socket_unwatch_control_pid(Socket *s) { @@ -227,7 +229,6 @@ int socket_instantiate_service(Socket *s) { if (r < 0) return r; - u->no_gc = true; unit_ref_set(&s->service, u); return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); @@ -792,47 +793,45 @@ static void socket_close_fds(Socket *s) { assert(s); LIST_FOREACH(port, p, s->ports) { + bool was_open; - p->event_source = sd_event_source_unref(p->event_source); - - if (p->fd < 0) - continue; + was_open = p->fd >= 0; + p->event_source = sd_event_source_unref(p->event_source); p->fd = safe_close(p->fd); socket_cleanup_fd_list(p); - /* One little note: we should normally not delete any - * sockets in the file system here! After all some - * other process we spawned might still have a - * reference of this fd and wants to continue to use - * it. Therefore we delete sockets in the file system - * before we create a new one, not after we stopped - * using one! */ + /* One little note: we should normally not delete any sockets in the file system here! After all some + * other process we spawned might still have a reference of this fd and wants to continue to use + * it. Therefore we normally delete sockets in the file system before we create a new one, not after we + * stopped using one! That all said, if the user explicitly requested this, we'll delete them here + * anyway, but only then. */ - if (s->remove_on_stop) { - switch (p->type) { + if (!was_open || !s->remove_on_stop) + continue; - case SOCKET_FIFO: - unlink(p->path); - break; + switch (p->type) { - case SOCKET_MQUEUE: - mq_unlink(p->path); - break; + case SOCKET_FIFO: + (void) unlink(p->path); + break; - case SOCKET_SOCKET: - socket_address_unlink(&p->address); - break; + case SOCKET_MQUEUE: + (void) mq_unlink(p->path); + break; - default: - break; - } + case SOCKET_SOCKET: + (void) socket_address_unlink(&p->address); + break; + + default: + break; } } if (s->remove_on_stop) STRV_FOREACH(i, s->symlinks) - unlink(*i); + (void) unlink(*i); } static void socket_apply_socket_options(Socket *s, int fd) { @@ -1887,6 +1886,9 @@ static void socket_enter_running(Socket *s, int cfd) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; + /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or + * close it. */ + assert(s); /* We don't take connections anymore if we are supposed to @@ -1896,7 +1898,7 @@ static void socket_enter_running(Socket *s, int cfd) { log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); if (cfd >= 0) - safe_close(cfd); + cfd = safe_close(cfd); else { /* Flush all sockets by closing and reopening them */ socket_close_fds(s); @@ -1918,6 +1920,13 @@ static void socket_enter_running(Socket *s, int cfd) { return; } + if (!ratelimit_test(&s->trigger_limit)) { + safe_close(cfd); + log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); + socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); + return; + } + if (cfd < 0) { Iterator i; Unit *other; @@ -1949,7 +1958,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); safe_close(cfd); return; } @@ -1965,6 +1974,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* ENOTCONN is legitimate if TCP RST was received. * This connection is over, but the socket unit lives on. */ + log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); safe_close(cfd); return; } @@ -1983,22 +1993,24 @@ static void socket_enter_running(Socket *s, int cfd) { service = SERVICE(UNIT_DEREF(s->service)); unit_ref_unset(&s->service); - s->n_accepted++; - - UNIT(service)->no_gc = false; + s->n_accepted++; unit_choose_id(UNIT(service), name); r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); if (r < 0) goto fail; - cfd = -1; + cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); - if (r < 0) + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the service + * closes and forgets the connection fd again, immediately. */ + service_close_socket_fd(service); goto fail; + } /* Notify clients about changed counters */ unit_add_to_dbus_queue(UNIT(s)); @@ -2806,6 +2818,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { [SOCKET_FAILURE_EXIT_CODE] = "exit-code", [SOCKET_FAILURE_SIGNAL] = "signal", [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" }; diff --git a/src/core/socket.h b/src/core/socket.h index b537b026a7..2a4b1bb674 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -52,6 +52,7 @@ typedef enum SocketResult { SOCKET_FAILURE_EXIT_CODE, SOCKET_FAILURE_SIGNAL, SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_TRIGGER_LIMIT_HIT, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, _SOCKET_RESULT_MAX, _SOCKET_RESULT_INVALID = -1 @@ -156,6 +157,8 @@ struct Socket { bool reset_cpu_usage:1; char *fdname; + + RateLimit trigger_limit; }; /* Called from the service code when collecting fds */ diff --git a/src/core/swap.c b/src/core/swap.c index d8802470d2..c018648d87 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1465,9 +1465,6 @@ const UnitVTable swap_vtable = { "Install\0", .private_section = "Swap", - .no_alias = true, - .no_instances = true, - .init = swap_init, .load = swap_load, .done = swap_done, diff --git a/src/core/system.conf b/src/core/system.conf index e2ded27333..eacd7ee282 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -34,7 +34,7 @@ #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s #DefaultRestartSec=100ms -#DefaultStartLimitInterval=10s +#DefaultStartLimitIntervalSec=10s #DefaultStartLimitBurst=5 #DefaultEnvironment= #DefaultCPUAccounting=no diff --git a/src/core/unit.c b/src/core/unit.c index cb79c7c6b1..4ace6b075b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -193,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) { if (r < 0) return r; - if (i && unit_vtable[t]->no_instances) + if (i && !unit_type_may_template(t)) return -EINVAL; /* Ensure that this unit is either instanced or not instanced, @@ -202,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) { if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) return -EINVAL; - if (unit_vtable[t]->no_alias && !set_isempty(u->names)) + if (!unit_type_may_alias(t) && !set_isempty(u->names)) return -EEXIST; if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) @@ -720,6 +720,9 @@ int unit_merge(Unit *u, Unit *other) { if (!u->instance != !other->instance) return -EINVAL; + if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ + return -EEXIST; + if (other->load_state != UNIT_STUB && other->load_state != UNIT_NOT_FOUND) return -EEXIST; @@ -776,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) { } int unit_merge_by_name(Unit *u, const char *name) { + _cleanup_free_ char *s = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name); @@ -1497,11 +1500,6 @@ int unit_start(Unit *u) { if (UNIT_IS_ACTIVE_OR_RELOADING(state)) return -EALREADY; - /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); - if (r < 0) - return r; - /* Units that aren't loaded cannot be started */ if (u->load_state != UNIT_LOADED) return -EINVAL; @@ -1543,6 +1541,11 @@ int unit_start(Unit *u) { if (!UNIT_VTABLE(u)->start) return -EBADR; + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_start_limit_test(u); + if (r < 0) + return r; + /* We don't suppress calls to ->start() here when we are * already starting, to allow this request to be used as a * "hurry up" call, for example when the unit is in some "auto @@ -3222,6 +3225,10 @@ void unit_ref_unset(UnitRef *ref) { if (!ref->unit) return; + /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might + * be unreferenced now. */ + unit_add_to_gc_queue(ref->unit); + LIST_REMOVE(refs, ref->unit->refs, ref); ref->unit = NULL; } diff --git a/src/core/unit.h b/src/core/unit.h index 5909652976..be62e88421 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -416,12 +416,6 @@ struct UnitVTable { /* The strings to print in status messages */ UnitStatusMessageFormats status_message_formats; - /* Can units of this type have multiple names? */ - bool no_alias:1; - - /* Instances make no sense for this type */ - bool no_instances:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; diff --git a/src/core/user.conf b/src/core/user.conf index 87c8164378..b427f1ef6d 100644 --- a/src/core/user.conf +++ b/src/core/user.conf @@ -23,7 +23,7 @@ #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s #DefaultRestartSec=100ms -#DefaultStartLimitInterval=10s +#DefaultStartLimitIntervalSec=10s #DefaultStartLimitBurst=5 #DefaultEnvironment= #DefaultLimitCPU= diff --git a/src/import/pull-common.c b/src/import/pull-common.c index d301d4d79e..dc4e4667a9 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -330,7 +330,7 @@ int pull_verify(PullJob *main_job, _cleanup_close_ int sig_file = -1; const char *p, *line; char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_sigkill_wait_ pid_t pid = 0; + _cleanup_(sigkill_waitp) pid_t pid = 0; bool gpg_home_created = false; int r; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index bed825cdc3..ec50333c2c 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -37,6 +37,7 @@ #include "journal-file.h" #include "lookup3.h" #include "parse-util.h" +#include "path-util.h" #include "random-util.h" #include "sd-event.h" #include "set.h" @@ -119,7 +120,7 @@ static void journal_file_set_offline_internal(JournalFile *f) { if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING)) continue; - f->header->state = STATE_OFFLINE; + f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE; (void) fsync(f->fd); break; @@ -217,8 +218,10 @@ int journal_file_set_offline(JournalFile *f, bool wait) { if (!(f->fd >= 0 && f->header)) return -EINVAL; - if (f->header->state != STATE_ONLINE) - return 0; + /* An offlining journal is implicitly online and may modify f->header->state, + * we must also join any potentially lingering offline thread when not online. */ + if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) + return journal_file_set_offline_thread_join(f); /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ restarted = journal_file_set_offline_try_restart(f); @@ -362,7 +365,8 @@ JournalFile* journal_file_close(JournalFile *f) { (void) btrfs_defrag_fd(f->fd); } - safe_close(f->fd); + if (f->close_fd) + safe_close(f->fd); free(f->path); mmap_cache_unref(f->mmap); @@ -435,6 +439,39 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { return 0; } +static int fsync_directory_of_file(int fd) { + _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_close_ int dfd = -1; + struct stat st; + int r; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADFD; + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return -EINVAL; + + dn = dirname_malloc(path); + if (!dn) + return -ENOMEM; + + dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + return -errno; + + if (fsync(dfd) < 0) + return -errno; + + return 0; +} + static int journal_file_refresh_header(JournalFile *f) { sd_id128_t boot_id; int r; @@ -460,6 +497,9 @@ static int journal_file_refresh_header(JournalFile *f) { /* Sync the online state to disk */ (void) fsync(f->fd); + /* We likely just created a new file, also sync the directory this file is located in. */ + (void) fsync_directory_of_file(f->fd); + return r; } @@ -703,7 +743,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset /* Objects may only be located at multiple of 64 bit */ if (!VALID64(offset)) - return -EFAULT; + return -EBADMSG; + + /* Object may not be located in the file header */ + if (offset < le64toh(f->header->header_size)) + return -EBADMSG; r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); if (r < 0) @@ -1976,9 +2020,14 @@ static int generic_array_bisect( i = right - 1; lp = p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); + n = i; + continue; + } if (r < 0) return r; @@ -2054,9 +2103,14 @@ static int generic_array_bisect( p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); + right = n = i; + continue; + } if (r < 0) return r; @@ -2461,13 +2515,18 @@ int journal_file_next_entry( le64toh(f->header->entry_array_offset), i, ret, &ofs); + if (r == -EBADMSG && direction == DIRECTION_DOWN) { + /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, + * consider this the end of the journal file. */ + log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); + return 0; + } if (r <= 0) return r; if (p > 0 && (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { - log_debug("%s: entry array corrupted at entry %"PRIu64, - f->path, i); + log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); return -EBADMSG; } @@ -2806,11 +2865,11 @@ void journal_file_print_header(JournalFile *f) { "Data Hash Table Size: %"PRIu64"\n" "Field Hash Table Size: %"PRIu64"\n" "Rotate Suggested: %s\n" - "Head Sequential Number: %"PRIu64"\n" - "Tail Sequential Number: %"PRIu64"\n" - "Head Realtime Timestamp: %s\n" - "Tail Realtime Timestamp: %s\n" - "Tail Monotonic Timestamp: %s\n" + "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Head Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Monotonic Timestamp: %s (%"PRIx64")\n" "Objects: %"PRIu64"\n" "Entry Objects: %"PRIu64"\n", f->path, @@ -2831,11 +2890,11 @@ void journal_file_print_header(JournalFile *f) { le64toh(f->header->data_hash_table_size) / sizeof(HashItem), le64toh(f->header->field_hash_table_size) / sizeof(HashItem), yes_no(journal_file_rotate_suggested(f, 0)), - le64toh(f->header->head_entry_seqnum), - le64toh(f->header->tail_entry_seqnum), - format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), - format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), - format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), + le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), + le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), + format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), + format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), + format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), le64toh(f->header->n_objects), le64toh(f->header->n_entries)); @@ -2898,6 +2957,7 @@ static int journal_file_warn_btrfs(JournalFile *f) { } int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, @@ -2914,22 +2974,24 @@ int journal_file_open( void *h; int r; - assert(fname); assert(ret); + assert(fd >= 0 || fname); if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) return -EINVAL; - if (!endswith(fname, ".journal") && - !endswith(fname, ".journal~")) - return -EINVAL; + if (fname) { + if (!endswith(fname, ".journal") && + !endswith(fname, ".journal~")) + return -EINVAL; + } f = new0(JournalFile, 1); if (!f) return -ENOMEM; - f->fd = -1; + f->fd = fd; f->mode = mode; f->flags = flags; @@ -2954,7 +3016,10 @@ int journal_file_open( } } - f->path = strdup(fname); + if (fname) + f->path = strdup(fname); + else /* If we don't know the path, fill in something explanatory and vaguely useful */ + asprintf(&f->path, "/proc/self/%i", fd); if (!f->path) { r = -ENOMEM; goto fail; @@ -2966,10 +3031,15 @@ int journal_file_open( goto fail; } - f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); if (f->fd < 0) { - r = -errno; - goto fail; + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + /* fds we opened here by us should also be closed by us. */ + f->close_fd = true; } r = journal_file_fstat(f); @@ -3090,6 +3160,9 @@ int journal_file_open( goto fail; } + /* The file is opened now successfully, thus we take possesion of any passed in fd. */ + f->close_fd = true; + *ret = f; return 0; @@ -3116,6 +3189,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred if (!old_file->writable) return -EINVAL; + /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse + * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ + if (path_startswith(old_file->path, "/proc/self/fd")) + return -EINVAL; + if (!endswith(old_file->path, ".journal")) return -EINVAL; @@ -3135,14 +3213,23 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred if (r < 0 && errno != ENOENT) return -errno; - old_file->header->state = STATE_ARCHIVED; + /* Sync the rename to disk */ + (void) fsync_directory_of_file(old_file->fd); + + /* Set as archive so offlining commits w/state=STATE_ARCHIVED. + * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, + * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which + * would result in the rotated journal never getting fsync() called before closing. + * Now we simply queue the archive state by setting an archive bit, leaving the state + * as STATE_ONLINE so proper offlining occurs. */ + old_file->archive = true; /* Currently, btrfs is not very good with out write patterns * and fragments heavily. Let's defrag our journal files when * we archive them */ old_file->defrag_on_close = true; - r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); + r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); if (deferred_closes && set_put(deferred_closes, old_file) >= 0) @@ -3170,7 +3257,7 @@ int journal_file_open_reliably( size_t l; _cleanup_free_ char *p = NULL; - r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); if (!IN_SET(r, -EBADMSG, /* corrupted */ -ENODATA, /* truncated */ @@ -3206,12 +3293,12 @@ int journal_file_open_reliably( /* btrfs doesn't cope well with our write pattern and * fragments heavily. Let's defrag all files we rotate */ - (void) chattr_path(p, false, FS_NOCOW_FL); + (void) chattr_path(p, 0, FS_NOCOW_FL); (void) btrfs_defrag(p); log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); - return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); } int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 9ad6013359..564e1a8179 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -85,6 +85,8 @@ typedef struct JournalFile { bool compress_lz4:1; bool seal:1; bool defrag_on_close:1; + bool close_fd:1; + bool archive:1; bool tail_entry_monotonic_valid:1; @@ -142,6 +144,7 @@ typedef struct JournalFile { } JournalFile; int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 7639325acf..34a48141f5 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -82,6 +82,8 @@ struct Directory { }; struct sd_journal { + int toplevel_fd; + char *path; char *prefix; @@ -117,6 +119,7 @@ struct sd_journal { bool on_network:1; bool no_new_files:1; + bool no_inotify:1; bool unique_file_lost:1; /* File we were iterating over got removed, and there were no more files, so sd_j_enumerate_unique diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index c9a2c3812d..f67c556783 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -101,6 +101,7 @@ static const char *arg_after_cursor = NULL; static bool arg_show_cursor = false; static const char *arg_directory = NULL; static char **arg_file = NULL; +static bool arg_file_stdin = false; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT @@ -592,9 +593,17 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_FILE: - r = glob_extend(&arg_file, optarg); - if (r < 0) - return log_error_errno(r, "Failed to add paths: %m"); + if (streq(optarg, "-")) + /* An undocumented feature: we can read journal files from STDIN. We don't document + * this though, since after all we only support this for mmap-able, seekable files, and + * not for example pipes which are probably the primary usecase for reading things from + * STDIN. To avoid confusion we hence don't document this feature. */ + arg_file_stdin = true; + else { + r = glob_extend(&arg_file, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); + } break; case ARG_ROOT: @@ -864,6 +873,18 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) { + + /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user + * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice + * to users, and automatically turn --unit= into --user-unit= if combined with --user. */ + r = strv_extend_strv(&arg_user_units, arg_system_units, true); + if (r < 0) + return -ENOMEM; + + arg_system_units = strv_free(arg_system_units); + } + return 1; } @@ -988,18 +1009,18 @@ static void boot_id_free_all(BootId *l) { } } -static int discover_next_boot( - sd_journal *j, - BootId **boot, +static int discover_next_boot(sd_journal *j, + sd_id128_t previous_boot_id, bool advance_older, - bool read_realtime) { + BootId **ret) { - int r; - char match[9+32+1] = "_BOOT_ID="; _cleanup_free_ BootId *next_boot = NULL; + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; assert(j); - assert(boot); + assert(ret); /* We expect the journal to be on the last position of a boot * (in relation to the direction we are going), so that the next @@ -1012,29 +1033,40 @@ static int discover_next_boot( * we can actually advance to a *different* boot. */ sd_journal_flush_matches(j); - if (advance_older) - r = sd_journal_previous(j); - else - r = sd_journal_next(j); - if (r < 0) - return r; - else if (r == 0) - return 0; /* End of journal, yay. */ + do { + if (advance_older) + r = sd_journal_previous(j); + else + r = sd_journal_next(j); + if (r < 0) + return r; + else if (r == 0) + return 0; /* End of journal, yay. */ + + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r < 0) + return r; + + /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that + * normally, this will only require a single iteration, as we seeked to the last entry of the previous + * boot entry already. However, it might happen that the per-journal-field entry arrays are less + * complete than the main entry array, and hence might reference an entry that's not actually the last + * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to + * speed things up, but let's not trust that it is complete, and hence, manually advance as + * necessary. */ + + } while (sd_id128_equal(boot_id, previous_boot_id)); next_boot = new0(BootId, 1); if (!next_boot) return -ENOMEM; - r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id); + next_boot->id = boot_id; + + r = sd_journal_get_realtime_usec(j, &next_boot->first); if (r < 0) return r; - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->first); - if (r < 0) - return r; - } - /* Now seek to the last occurrence of this boot ID. */ sd_id128_to_string(next_boot->id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); @@ -1057,13 +1089,11 @@ static int discover_next_boot( else if (r == 0) return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->last); - if (r < 0) - return r; - } + r = sd_journal_get_realtime_usec(j, &next_boot->last); + if (r < 0) + return r; - *boot = next_boot; + *ret = next_boot; next_boot = NULL; return 0; @@ -1072,47 +1102,48 @@ static int discover_next_boot( static int get_boots( sd_journal *j, BootId **boots, - BootId *query_ref_boot, + sd_id128_t *query_ref_boot, int ref_boot_offset) { bool skip_once; int r, count = 0; BootId *head = NULL, *tail = NULL; const bool advance_older = query_ref_boot && ref_boot_offset <= 0; + sd_id128_t previous_boot_id; assert(j); /* Adjust for the asymmetry that offset 0 is * the last (and current) boot, while 1 is considered the * (chronological) first boot in the journal. */ - skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0; + skip_once = query_ref_boot && sd_id128_is_null(*query_ref_boot) && ref_boot_offset < 0; /* Advance to the earliest/latest occurrence of our reference * boot ID (taking our lookup direction into account), so that * discover_next_boot() can do its job. * If no reference is given, the journal head/tail will do, * they're "virtual" boots after all. */ - if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) { + if (query_ref_boot && !sd_id128_is_null(*query_ref_boot)) { char match[9+32+1] = "_BOOT_ID="; sd_journal_flush_matches(j); - sd_id128_to_string(query_ref_boot->id, match + 9); + sd_id128_to_string(*query_ref_boot, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) return r; if (advance_older) - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ else - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ if (r < 0) return r; if (advance_older) - r = sd_journal_next(j); + r = sd_journal_next(j); /* read the oldest entry */ else - r = sd_journal_previous(j); + r = sd_journal_previous(j); /* read the most recently added entry */ if (r < 0) return r; else if (r == 0) @@ -1121,21 +1152,31 @@ static int get_boots( count = 1; goto finish; } + + /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot + * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at + * the following entry, which must then have an older/newer boot ID */ } else { + if (advance_older) - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ else - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ if (r < 0) return r; - /* No sd_journal_next/previous here. */ + /* No sd_journal_next()/_previous() here. + * + * At this point the read pointer is positioned after the newest/before the oldest entry in the whole + * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest + * entry we have. */ } + previous_boot_id = SD_ID128_NULL; for (;;) { _cleanup_free_ BootId *current = NULL; - r = discover_next_boot(j, ¤t, advance_older, !query_ref_boot); + r = discover_next_boot(j, previous_boot_id, advance_older, ¤t); if (r < 0) { boot_id_free_all(head); return r; @@ -1144,6 +1185,8 @@ static int get_boots( if (!current) break; + previous_boot_id = current->id; + if (query_ref_boot) { if (!skip_once) ref_boot_offset += advance_older ? 1 : -1; @@ -1151,7 +1194,7 @@ static int get_boots( if (ref_boot_offset == 0) { count = 1; - query_ref_boot->id = current->id; + *query_ref_boot = current->id; break; } } else { @@ -1207,8 +1250,8 @@ static int list_boots(sd_journal *j) { static int add_boot(sd_journal *j) { char match[9+32+1] = "_BOOT_ID="; + sd_id128_t ref_boot_id; int r; - BootId ref_boot_id = {}; assert(j); @@ -1218,7 +1261,7 @@ static int add_boot(sd_journal *j) { if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL)) return add_match_this_boot(j, arg_machine); - ref_boot_id.id = arg_boot_id; + ref_boot_id = arg_boot_id; r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset); assert(r <= 1); if (r <= 0) { @@ -1234,7 +1277,7 @@ static int add_boot(sd_journal *j) { return r == 0 ? -ENODATA : r; } - sd_id128_to_string(ref_boot_id.id, match + 9); + sd_id128_to_string(ref_boot_id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) @@ -1847,7 +1890,7 @@ static int access_check(sd_journal *j) { break; default: - log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path); + log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); break; } } @@ -2103,11 +2146,61 @@ int main(int argc, char *argv[]) { if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); - else if (arg_file) + else if (arg_file_stdin) { + int ifd = STDIN_FILENO; + r = sd_journal_open_files_fd(&j, &ifd, 1, 0); + } else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); - else if (arg_machine) - r = sd_journal_open_container(&j, arg_machine, 0); - else + else if (arg_machine) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int fd; + + if (geteuid() != 0) { + /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of + * the container, thus we need root privileges to override them. */ + log_error("Using the --machine= switch requires root privileges."); + r = -EPERM; + goto finish; + } + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error_errno(r, "Failed to open system bus: %m"); + goto finish; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineRootDirectory", + &error, + &reply, + "s", arg_machine); + if (r < 0) { + log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r)); + goto finish; + } + + r = sd_bus_message_read(reply, "h", &fd); + if (r < 0) { + bus_log_parse_error(r); + goto finish; + } + + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) { + r = log_error_errno(errno, "Failed to duplicate file descriptor: %m"); + goto finish; + } + + r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT); + if (r < 0) + safe_close(fd); + } else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal"); @@ -2283,6 +2376,10 @@ int main(int argc, char *argv[]) { /* Opening the fd now means the first sd_journal_wait() will actually wait */ if (arg_follow) { r = sd_journal_get_fd(j); + if (r == -EMEDIUMTYPE) { + log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); + goto finish; + } if (r < 0) { log_error_errno(r, "Failed to get journal fd: %m"); goto finish; diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index c154610c54..7fecd7a964 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -19,7 +19,9 @@ Journal.Storage, config_parse_storage, 0, offsetof(Server, storage Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) +# The following is a legacy name for compatibility Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) +Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval) Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use) Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size) diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 3d8f05996b..a445291a5e 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -206,7 +206,7 @@ void server_process_native_message( allow_object_pid(ucred)) { char buf[DECIMAL_STR_MAX(pid_t)]; memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); - char_array_0(buf); + buf[l-strlen("OBJECT_PID=")] = '\0'; /* ignore error */ parse_pid(buf, &object_pid); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 8089bb5883..e14d0ad980 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -253,7 +253,7 @@ static int open_journal( if (reliably) r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); else - r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); + r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); if (r < 0) return r; diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 7beb96c671..2541b949be 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -17,7 +17,7 @@ #Seal=yes #SplitMode=uid #SyncIntervalSec=5m -#RateLimitInterval=30s +#RateLimitIntervalSec=30s #RateLimitBurst=1000 #SystemMaxUse= #SystemKeepFree= diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 3c21d4129e..27c1dd346f 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1233,14 +1233,37 @@ static bool file_type_wanted(int flags, const char *filename) { return false; } -static int add_any_file(sd_journal *j, const char *path) { +static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { + assert(j); + assert(path); + assert(prefix); + + if (j->toplevel_fd >= 0) + return false; + + return path_startswith(path, prefix); +} + +static const char *skip_slash(const char *p) { + + if (!p) + return NULL; + + while (*p == '/') + p++; + + return p; +} + +static int add_any_file(sd_journal *j, int fd, const char *path) { JournalFile *f = NULL; + bool close_fd = false; int r, k; assert(j); - assert(path); + assert(fd >= 0 || path); - if (ordered_hashmap_get(j->files, path)) + if (path && ordered_hashmap_get(j->files, path)) return 0; if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { @@ -1249,8 +1272,24 @@ static int add_any_file(sd_journal *j, const char *path) { goto fail; } - r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); + if (fd < 0 && j->toplevel_fd >= 0) { + + /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, + * explicitly, since otherwise openat() ignores the first argument.) */ + + fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); + goto fail; + } + + close_fd = true; + } + + r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); if (r < 0) { + if (close_fd) + safe_close(fd); log_debug_errno(r, "Failed to open journal file %s: %m", path); goto fail; } @@ -1259,10 +1298,16 @@ static int add_any_file(sd_journal *j, const char *path) { r = ordered_hashmap_put(j->files, f->path, f); if (r < 0) { + f->close_fd = close_fd; (void) journal_file_close(f); goto fail; } + if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) + j->has_runtime_files = true; + else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) + j->has_persistent_files = true; + log_debug("File %s added.", f->path); check_network(j, f->fd); @@ -1286,18 +1331,14 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { assert(prefix); assert(filename); - if (j->no_new_files || - !file_type_wanted(j->flags, filename)) + if (j->no_new_files) + return 0; + + if (!file_type_wanted(j->flags, filename)) return 0; path = strjoina(prefix, "/", filename); - - if (!j->has_runtime_files && path_startswith(path, "/run/log/journal")) - j->has_runtime_files = true; - else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal")) - j->has_persistent_files = true; - - return add_any_file(j, path); + return add_any_file(j, -1, path); } static void remove_file(sd_journal *j, const char *prefix, const char *filename) { @@ -1373,21 +1414,33 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) assert(j); assert(prefix); - assert(dirname); - - log_debug("Considering %s/%s.", prefix, dirname); - if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && - !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run"))) - return 0; + /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch + * and reenumerates directory contents */ - path = strjoin(prefix, "/", dirname, NULL); + if (dirname) + path = strjoin(prefix, "/", dirname, NULL); + else + path = strdup(prefix); if (!path) { r = -ENOMEM; goto fail; } - d = opendir(path); + log_debug("Considering directory %s.", path); + + /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) + return 0; + + + if (j->toplevel_fd < 0) + d = opendir(path); + else + /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is + * relative, by dropping the initial slash */ + d = xopendirat(j->toplevel_fd, skip_slash(path), 0); if (!d) { r = log_debug_errno(errno, "Failed to open directory %s: %m", path); goto fail; @@ -1419,17 +1472,18 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; if (m->wd <= 0 && j->inotify_fd >= 0) { + /* Watch this directory, if it not being watched yet. */ - m->wd = inotify_add_watch(j->inotify_fd, m->path, - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| - IN_ONLYDIR); + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + IN_ONLYDIR); if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) inotify_rm_watch(j->inotify_fd, m->wd); } - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) @@ -1441,7 +1495,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; fail: - k = journal_put_error(j, r, path ?: dirname); + k = journal_put_error(j, r, path ?: prefix); if (k < 0) return k; @@ -1449,28 +1503,62 @@ fail: } static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; Directory *m; int r, k; assert(j); - assert(p); - if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && - !path_startswith(p, "/run")) - return -EINVAL; + /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we + * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially + * populate the set, as well as to update it later. */ - if (j->prefix) - p = strjoina(j->prefix, p); + if (p) { + /* If there's a path specified, use it. */ - d = opendir(p); - if (!d) { - if (errno == ENOENT && missing_ok) - return 0; + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_has_prefix(j, p, "/run")) + return -EINVAL; - r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); - goto fail; + if (j->prefix) + p = strjoina(j->prefix, p); + + if (j->toplevel_fd < 0) + d = opendir(p); + else + d = xopendirat(j->toplevel_fd, skip_slash(p), 0); + + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } + } else { + int dfd; + + /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since + * opendir() will take possession of the fd, and close it, which we don't want. */ + + p = "."; /* store this as "." in the directories hashmap */ + + dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); + if (dfd < 0) { + r = -errno; + goto fail; + } + + d = fdopendir(dfd); + if (!d) { + r = -errno; + safe_close(dfd); + goto fail; + } + + rewinddir(d); } m = hashmap_get(j->directories_by_path, p); @@ -1482,6 +1570,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { } m->is_root = true; + m->path = strdup(p); if (!m->path) { free(m); @@ -1505,7 +1594,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (m->wd <= 0 && j->inotify_fd >= 0) { - m->wd = inotify_add_watch(j->inotify_fd, m->path, + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| IN_ONLYDIR); @@ -1516,7 +1605,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (j->no_new_files) return 0; - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { sd_id128_t id; if (dirent_is_file_with_suffix(de, ".journal") || @@ -1585,8 +1674,7 @@ static int add_current_paths(sd_journal *j) { assert(j); assert(j->no_new_files); - /* Simply adds all directories for files we have open as - * "root" directories. We don't expect errors here, so we + /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we * treat them as fatal. */ ORDERED_HASHMAP_FOREACH(f, j->files, i) { @@ -1597,7 +1685,7 @@ static int add_current_paths(sd_journal *j) { if (!dir) return -ENOMEM; - r = add_root_directory(j, dir, true); + r = add_directory(j, dir, NULL); if (r < 0) return r; } @@ -1614,13 +1702,7 @@ static int allocate_inotify(sd_journal *j) { return -errno; } - if (!j->directories_by_wd) { - j->directories_by_wd = hashmap_new(NULL); - if (!j->directories_by_wd) - return -ENOMEM; - } - - return 0; + return hashmap_ensure_allocated(&j->directories_by_wd, NULL); } static sd_journal *journal_new(int flags, const char *path) { @@ -1631,6 +1713,7 @@ static sd_journal *journal_new(int flags, const char *path) { return NULL; j->original_pid = getpid(); + j->toplevel_fd = -1; j->inotify_fd = -1; j->flags = flags; j->data_threshold = DEFAULT_DATA_THRESHOLD; @@ -1684,6 +1767,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in char *p; int r; + /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + * combination with sd_journal_open_directory_fd(). */ + assert_return(machine, -EINVAL); assert_return(ret, -EINVAL); assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); @@ -1726,13 +1812,16 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f assert_return(ret, -EINVAL); assert_return(path, -EINVAL); - assert_return(flags == 0, -EINVAL); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); j = journal_new(flags, path); if (!j) return -ENOMEM; - r = add_root_directory(j, path, false); + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, path, false); if (r < 0) goto fail; @@ -1741,7 +1830,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f fail: sd_journal_close(j); - return r; } @@ -1758,7 +1846,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla return -ENOMEM; STRV_FOREACH(path, paths) { - r = add_any_file(j, *path); + r = add_any_file(j, -1, *path); if (r < 0) goto fail; } @@ -1770,7 +1858,96 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla fail: sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { + sd_journal *j; + struct stat st; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EBADFD; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->toplevel_fd = fd; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, NULL, false); + if (r < 0) + goto fail; + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { + Iterator iterator; + JournalFile *f; + sd_journal *j; + unsigned i; + int r; + + assert_return(ret, -EINVAL); + assert_return(n_fds > 0, -EBADF); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + struct stat st; + + if (fds[i] < 0) { + r = -EBADF; + goto fail; + } + + if (fstat(fds[i], &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISREG(st.st_mode)) { + r = -EBADFD; + goto fail; + } + + r = add_any_file(j, fds[i], NULL); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + j->no_inotify = true; + + *ret = j; + return 0; + +fail: + /* If we fail, make sure we don't take possession of the files we managed to make use of successfuly, and they + * remain open */ + ORDERED_HASHMAP_FOREACH(f, j->files, iterator) + f->close_fd = false; + + sd_journal_close(j); return r; } @@ -2097,6 +2274,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); + if (j->no_inotify) + return -EMEDIUMTYPE; + if (j->inotify_fd >= 0) return j->inotify_fd; @@ -2104,10 +2284,14 @@ _public_ int sd_journal_get_fd(sd_journal *j) { if (r < 0) return r; + log_debug("Reiterating files to get inotify watches established"); + /* Iterate through all dirs again, to add them to the * inotify */ if (j->no_new_files) r = add_current_paths(j); + else if (j->toplevel_fd >= 0) + r = add_root_directory(j, NULL, false); else if (j->path) r = add_root_directory(j, j->path, true); else diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c index 93dc0e0d81..ba8b20b228 100644 --- a/src/journal/test-journal-flush.c +++ b/src/journal/test-journal-flush.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(dn)); fn = strappend(dn, "/test.journal"); - r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); + r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); assert_se(r >= 0); r = sd_journal_open(&j, 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index f887f43f0d..5e063f4d04 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -52,7 +52,7 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil static JournalFile *test_open(const char *name) { JournalFile *f; - assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); + assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); return f; } @@ -216,7 +216,7 @@ static void test_sequence_numbers(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644, + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &one) == 0); append_number(one, 1, &seqnum); @@ -233,7 +233,7 @@ static void test_sequence_numbers(void) { memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t)); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644, + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, one, &two) == 0); assert_se(two->header->state == STATE_ONLINE); @@ -264,7 +264,7 @@ static void test_sequence_numbers(void) { /* restart server */ seqnum = 0; - assert_se(journal_file_open("two.journal", O_RDWR, 0, + assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0, true, false, NULL, NULL, NULL, NULL, &two) == 0); assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id)); diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 839ea5a9a5..7e5a980719 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -92,9 +92,9 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); - assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); + assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); for (i = 0; i < N_ENTRIES; i++) { char *p, *q; diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 6b4643cd25..3d2312fc55 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -55,7 +55,7 @@ static int raw_verify(const char *fn, const char *verification_key) { JournalFile *f; int r; - r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); + r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); if (r < 0) return r; @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) { log_info("Generating..."); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); for (n = 0; n < N_ENTRIES; n++) { struct iovec iovec; @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); - assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); /* journal_file_print_header(f); */ journal_file_dump(f); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index ea685af782..2543d64b5b 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -42,7 +42,7 @@ static void test_non_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); dual_timestamp_get(&ts); @@ -131,13 +131,13 @@ static void test_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); - assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); + assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); - assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); + assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); - assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); + assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); journal_file_print_header(f1); puts(""); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 4ab637b686..0b3a1708dc 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -489,3 +489,9 @@ global: sd_journal_enumerate_fields; sd_journal_restart_fields; } LIBSYSTEMD_227; + +LIBSYSTEMD_230 { +global: + sd_journal_open_directory_fd; + sd_journal_open_files_fd; +} LIBSYSTEMD_229; diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map index 6aadda091a..d0b02a6b98 100644 --- a/src/locale/language-fallback-map +++ b/src/locale/language-fallback-map @@ -3,6 +3,10 @@ en_AU en_AU:en_GB en_IE en_IE:en_GB en_NZ en_NZ:en_GB en_ZA en_ZA:en_GB +fr_BE fr_BE:fr_FR +fr_CA fr_CA:fr_FR +fr_CH fr_CH:fr_FR +fr_LU fr_LU:fr_FR it_CH it_CH:it_IT mai_IN mai:hi nds_DE nds:de diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4 index 7933508f2b..f188a8e548 100644 --- a/src/login/systemd-user.m4 +++ b/src/login/systemd-user.m4 @@ -8,4 +8,5 @@ m4_ifdef(`HAVE_SELINUX', session required pam_selinux.so close session required pam_selinux.so nottys open )m4_dnl +session required pam_loginuid.so session include system-auth diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 73f5112c4d..0eed9b81bb 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -20,9 +20,11 @@ #include "alloc-util.h" #include "bus-label.h" #include "bus-util.h" +#include "fd-util.h" #include "image-dbus.h" #include "io-util.h" #include "machine-image.h" +#include "process-util.h" #include "strv.h" #include "user-util.h" @@ -33,13 +35,18 @@ int bus_image_method_remove( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; + pid_t child; int r; assert(message); assert(image); + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -54,11 +61,35 @@ int bus_image_method_remove( if (r == 0) return 1; /* Will call us back */ - r = image_remove(image); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_remove(image); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_rename( @@ -107,13 +138,19 @@ int bus_image_method_clone( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; const char *new_name; int r, read_only; + pid_t child; assert(message); assert(image); + assert(m); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); r = sd_bus_message_read(message, "sb", &new_name, &read_only); if (r < 0) @@ -136,11 +173,35 @@ int bus_image_method_clone( if (r == 0) return 1; /* Will call us back */ - r = image_clone(image, new_name, read_only); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_clone(image, new_name, read_only); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_mark_read_only( diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index ab54d9e934..7b9aa66d63 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1085,52 +1085,11 @@ finish: return r; } -static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - MachineOperation *o = userdata; - int r; - - assert(o); - assert(si); - - o->pid = 0; - - if (si->si_code != CLD_EXITED) { - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto fail; - } - - if (si->si_status != EXIT_SUCCESS) { - if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) - r = sd_bus_error_set_errnof(&error, r, "%m"); - else - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - - goto fail; - } - - r = sd_bus_reply_method_return(o->message, NULL); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; - -fail: - r = sd_bus_reply_method_error(o->message, &error); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; -} - int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; _cleanup_close_ int hostfd = -1; Machine *m = userdata; - MachineOperation *o; bool copy_from; pid_t child; char *t; @@ -1139,7 +1098,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); assert(m); - if (m->n_operations >= MACHINE_OPERATIONS_MAX) + if (m->manager->n_operations >= OPERATIONS_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); if (m->class != MACHINE_CONTAINER) @@ -1249,29 +1208,107 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - /* Copying might take a while, hence install a watch the - * child, and return */ + /* Copying might take a while, hence install a watch on the child, and return */ - o = new0(MachineOperation, 1); - if (!o) - return log_oom(); - - o->pid = child; - o->message = sd_bus_message_ref(message); - o->errno_fd = errno_pipe_fd[0]; + r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } errno_pipe_fd[0] = -1; - r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o); - if (r < 0) { - machine_operation_unref(o); - return log_oom(); + return 1; +} + +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_close_ int fd = -1; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + switch (m->class) { + + case MACHINE_HOST: + fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + break; + + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + siginfo_t si; + pid_t child; + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int dfd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + _exit(EXIT_FAILURE); + + r = send_one_fd(pair[1], dfd, 0); + dfd = safe_close(dfd); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + fd = receive_one_fd(pair[0], MSG_DONTWAIT); + if (fd < 0) + return fd; + + break; } - LIST_PREPEND(operations, m->operations, o); - m->n_operations++; - o->machine = m; + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); + } - return 1; + return sd_bus_reply_method_return(message, "h", fd); } const sd_bus_vtable machine_vtable[] = { @@ -1297,6 +1334,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index 3a8162b171..241b23c7ec 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -38,6 +38,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index 7d4270a8ff..c1fae57084 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -89,7 +89,7 @@ void machine_free(Machine *m) { assert(m); while (m->operations) - machine_operation_unref(m->operations); + operation_free(m->operations); if (m->in_gc_queue) LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); @@ -596,28 +596,6 @@ int machine_open_terminal(Machine *m, const char *path, int mode) { } } -MachineOperation *machine_operation_unref(MachineOperation *o) { - if (!o) - return NULL; - - sd_event_source_unref(o->event_source); - - safe_close(o->errno_fd); - - if (o->pid > 1) - (void) kill(o->pid, SIGKILL); - - sd_bus_message_unref(o->message); - - if (o->machine) { - LIST_REMOVE(operations, o->machine->operations, o); - o->machine->n_operations--; - } - - free(o); - return NULL; -} - void machine_release_unit(Machine *m) { assert(m); diff --git a/src/machine/machine.h b/src/machine/machine.h index 1d8cc5911a..e5d75361a9 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -20,11 +20,11 @@ ***/ typedef struct Machine Machine; -typedef struct MachineOperation MachineOperation; typedef enum KillWho KillWho; #include "list.h" #include "machined.h" +#include "operation.h" typedef enum MachineState { MACHINE_OPENING, /* Machine is being registered */ @@ -49,17 +49,6 @@ enum KillWho { _KILL_WHO_INVALID = -1 }; -#define MACHINE_OPERATIONS_MAX 64 - -struct MachineOperation { - Machine *machine; - pid_t pid; - sd_bus_message *message; - int errno_fd; - sd_event_source *event_source; - LIST_FIELDS(MachineOperation, operations); -}; - struct Machine { Manager *manager; @@ -88,10 +77,9 @@ struct Machine { int *netif; unsigned n_netif; - LIST_FIELDS(Machine, gc_queue); + LIST_HEAD(Operation, operations); - MachineOperation *operations; - unsigned n_operations; + LIST_FIELDS(Machine, gc_queue); }; Machine* machine_new(Manager *manager, MachineClass class, const char *name); @@ -109,8 +97,6 @@ void machine_release_unit(Machine *m); MachineState machine_get_state(Machine *u); -MachineOperation *machine_operation_unref(MachineOperation *o); - const char* machine_class_to_string(MachineClass t) _const_; MachineClass machine_class_from_string(const char *s) _pure_; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index b03198bbf1..1165ab5afa 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1076,6 +1076,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { static int copy_files(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *abs_host_path = NULL; char *dest, *host_path, *container_path; sd_bus *bus = userdata; @@ -1099,19 +1100,28 @@ static int copy_files(int argc, char *argv[], void *userdata) { host_path = abs_host_path; } - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - copy_from ? "CopyFromMachine" : "CopyToMachine", - &error, - NULL, + copy_from ? "CopyFromMachine" : "CopyToMachine"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, "sss", argv[1], copy_from ? container_path : host_path, copy_from ? host_path : container_path); if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r)); return 0; @@ -1393,7 +1403,6 @@ static int shell_machine(int argc, char *argv[], void *userdata) { } static int remove_image(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; int r, i; @@ -1402,19 +1411,27 @@ static int remove_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); for (i = 1; i < argc; i++) { - r = sd_bus_call_method( + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "RemoveImage", - &error, - NULL, - "s", argv[i]); - if (r < 0) { - log_error("Could not remove image: %s", bus_error_message(&error, -r)); - return r; - } + "RemoveImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", argv[i]); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r)); } return 0; @@ -1446,24 +1463,30 @@ static int rename_image(int argc, char *argv[], void *userdata) { static int clone_image(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; sd_bus *bus = userdata; int r; polkit_agent_open_if_enabled(); - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "CloneImage", - &error, - NULL, - "ssb", argv[1], argv[2], arg_read_only); - if (r < 0) { - log_error("Could not clone image: %s", bus_error_message(&error, -r)); - return r; - } + "CloneImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r)); return 0; } @@ -2516,14 +2539,9 @@ static int parse_argv(int argc, char *argv[]) { assert(argv); for (;;) { - const char *option_string; - - if (reorder) - option_string = "hp:als:H:M:qn:o:"; - else - option_string = "+hp:als:H:M:qn:o:"; + const char * const option_string = "+hp:als:H:M:qn:o:"; - c = getopt_long(argc, argv, option_string, options, NULL); + c = getopt_long(argc, argv, option_string + reorder, options, NULL); if (c < 0) { /* We generally are fine with the fact that getopt_long() reorders the command line, and looks * for switches after the main verb. However, for "shell" we really don't want that, since we diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index c9639c3cf2..31efa3695b 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -706,6 +706,26 @@ static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_e return bus_machine_method_copy(message, machine, error); } +static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_root_directory(message, machine, error); +} + static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(image_unrefp) Image* i = NULL; const char *name; @@ -1225,6 +1245,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machined.c b/src/machine/machined.c index f2c1966a6b..f7ceb5e603 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -70,6 +70,11 @@ void manager_free(Manager *m) { assert(m); + while (m->operations) + operation_free(m->operations); + + assert(m->n_operations == 0); + while ((machine = hashmap_first(m->machines))) machine_free(machine); @@ -336,6 +341,9 @@ int manager_startup(Manager *m) { static bool check_idle(void *userdata) { Manager *m = userdata; + if (m->operations) + return false; + manager_gc(m, true); return hashmap_isempty(m->machines); diff --git a/src/machine/machined.h b/src/machine/machined.h index e7d7dfdceb..7b9b148044 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -32,6 +32,7 @@ typedef struct Manager Manager; #include "image-dbus.h" #include "machine-dbus.h" #include "machine.h" +#include "operation.h" struct Manager { sd_event *event; @@ -49,6 +50,9 @@ struct Manager { LIST_HEAD(Machine, machine_gc_queue); Machine *host_machine; + + LIST_HEAD(Operation, operations); + unsigned n_operations; }; Manager *manager_new(void); diff --git a/src/machine/operation.c b/src/machine/operation.c new file mode 100644 index 0000000000..e6ddc41a55 --- /dev/null +++ b/src/machine/operation.c @@ -0,0 +1,131 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 "fd-util.h" +#include "operation.h" +#include "process-util.h" + +static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Operation *o = userdata; + int r; + + assert(o); + assert(si); + + log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i", + o->pid, + sigchld_code_to_string(si->si_code), si->si_status); + + o->pid = 0; + + if (si->si_code != CLD_EXITED) { + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + goto fail; + } + + if (si->si_status != EXIT_SUCCESS) { + if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) + r = sd_bus_error_set_errnof(&error, r, "%m"); + else + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); + + goto fail; + } + + r = sd_bus_reply_method_return(o->message, NULL); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; + +fail: + r = sd_bus_reply_method_error(o->message, &error); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; +} + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) { + Operation *o; + int r; + + assert(manager); + assert(child > 1); + assert(message); + assert(errno_fd >= 0); + + o = new0(Operation, 1); + if (!o) + return -ENOMEM; + + r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); + if (r < 0) { + free(o); + return r; + } + + o->pid = child; + o->message = sd_bus_message_ref(message); + o->errno_fd = errno_fd; + + LIST_PREPEND(operations, manager->operations, o); + manager->n_operations++; + o->manager = manager; + + if (machine) { + LIST_PREPEND(operations_by_machine, machine->operations, o); + o->machine = machine; + } + + log_debug("Started new operation " PID_FMT ".", child); + + /* At this point we took ownership of both the child and the errno file descriptor! */ + + return 0; +} + +Operation *operation_free(Operation *o) { + if (!o) + return NULL; + + sd_event_source_unref(o->event_source); + + safe_close(o->errno_fd); + + if (o->pid > 1) + (void) sigkill_wait(o->pid); + + sd_bus_message_unref(o->message); + + if (o->manager) { + LIST_REMOVE(operations, o->manager->operations, o); + o->manager->n_operations--; + } + + if (o->machine) + LIST_REMOVE(operations_by_machine, o->machine->operations, o); + + free(o); + return NULL; +} diff --git a/src/machine/operation.h b/src/machine/operation.h new file mode 100644 index 0000000000..7ca47bc3af --- /dev/null +++ b/src/machine/operation.h @@ -0,0 +1,47 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 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 <sys/types.h> + +#include "sd-bus.h" +#include "sd-event.h" + +#include "list.h" + +typedef struct Operation Operation; + +#include "machined.h" + +#define OPERATIONS_MAX 64 + +struct Operation { + Manager *manager; + Machine *machine; + pid_t pid; + sd_bus_message *message; + int errno_fd; + sd_event_source *event_source; + LIST_FIELDS(Operation, operations); + LIST_FIELDS(Operation, operations_by_machine); +}; + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd); +Operation *operation_free(Operation *o); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7f9a7268cc..429319da6b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -67,16 +67,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) { if (r < 0) return r; - address->network = network; - - LIST_APPEND(addresses, network->static_addresses, address); - if (section) { address->section = section; hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); } + address->network = network; + LIST_APPEND(addresses, network->static_addresses, address); + *ret = address; address = NULL; diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index 73a8d16b58..70f0121d6d 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -31,7 +31,7 @@ int manager_parse_config_file(Manager *m) { return config_parse_many(PKGSYSCONFDIR "/networkd.conf", CONF_PATHS_NULSTR("systemd/networkd.conf.d"), - "DUID\0", + "DHCP\0", config_item_perf_lookup, networkd_gperf_lookup, false, m); } @@ -57,7 +57,8 @@ int config_parse_duid_rawdata( const char *rvalue, void *data, void *userdata) { - int r, n1, n2, byte; + + int r; char *cbyte; const char *pduid = rvalue; Manager *m = userdata; @@ -72,71 +73,78 @@ int config_parse_duid_rawdata( assert(rvalue); assert(userdata); - duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type - : n->duid_type; + duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type : n->duid_type; if (duidtype == _DUID_TYPE_INVALID) duidtype = DUID_TYPE_RAW; switch (duidtype) { + case DUID_TYPE_LLT: /* RawData contains DUID-LLT link-layer address (offset 6) */ duid_start_offset = 6; break; + case DUID_TYPE_EN: /* RawData contains DUID-EN identifier (offset 4) */ duid_start_offset = 4; break; + case DUID_TYPE_LL: /* RawData contains DUID-LL link-layer address (offset 2) */ duid_start_offset = 2; break; + case DUID_TYPE_UUID: /* RawData specifies UUID (offset 0) - fall thru */ + case DUID_TYPE_RAW: /* First two bytes of RawData is DUID Type - fall thru */ + default: break; } if (duidtype != DUID_TYPE_RAW) - dhcp_duid_type = (uint16_t)duidtype; + dhcp_duid_type = (uint16_t) duidtype; /* RawData contains DUID in format " NN:NN:NN... " */ for (;;) { + int n1, n2; + uint32_t byte; + r = extract_first_word(&pduid, &cbyte, ":", 0); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to read DUID, ignoring assignment: %s.", rvalue); - goto exit; + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); + return 0; } if (r == 0) break; - if ((duid_start_offset + dhcp_duid_len) >= MAX_DUID_LEN) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Max DUID length exceeded, ignoring assignment: %s.", rvalue); - goto exit; + if (duid_start_offset + dhcp_duid_len >= MAX_DUID_LEN) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); + return 0; } len = strlen(cbyte); - if ((len == 0) || (len > 2)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); - goto exit; + if (len != 1 && len != 2) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); + return 0; } - n2 = 0; n1 = unhexchar(cbyte[0]); if (len == 2) n2 = unhexchar(cbyte[1]); - if ((n1 < 0) || (n2 < 0)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); - goto exit; + else + n2 = 0; + + if (n1 < 0 || n2 < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); + return 0; } - byte = (n1 << (4 * (len-1))) | n2; + + byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; /* If DUID_TYPE_RAW, first two bytes hold DHCP DUID type code */ - if ((duidtype == DUID_TYPE_RAW) && (count < 2)) { + if (duidtype == DUID_TYPE_RAW && count < 2) { dhcp_duid_type |= (byte << (8 * (1 - count))); count++; continue; @@ -159,6 +167,5 @@ int config_parse_duid_rawdata( memcpy(&n->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len); } -exit: return 0; } diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index 0625fb335b..afc71b4cb8 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -14,5 +14,5 @@ struct ConfigPerfItem; %struct-type %includes %% -DUID.Type, config_parse_duid_type, 0, offsetof(Manager, duid_type) -DUID.RawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_GLOBAL, offsetof(Manager, dhcp_duid) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid_type) +DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_GLOBAL, offsetof(Manager, dhcp_duid) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5101d553d5..5fc513bfda 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -576,8 +576,6 @@ static void link_set_state(Link *link, LinkState state) { link->state = state; link_send_changed(link, "AdministrativeState", NULL); - - return; } static void link_enter_unmanaged(Link *link) { @@ -1458,7 +1456,7 @@ static int link_acquire_ipv6_conf(Link *link) { return 0; } -static int link_acquire_conf(Link *link) { +static int link_acquire_ipv4_conf(Link *link) { int r; assert(link); @@ -1486,6 +1484,24 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } + return 0; +} + +static int link_acquire_conf(Link *link) { + int r; + + assert(link); + + r = link_acquire_ipv4_conf(link); + if (r < 0) + return r; + + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } + if (link_lldp_tx_enabled(link)) { r = link_lldp_tx_start(link); if (r < 0) @@ -2351,12 +2367,6 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; - - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { - r = link_acquire_ipv6_conf(link); - if (r < 0) - return r; - } } return link_enter_join_netdev(link); @@ -2739,6 +2749,10 @@ static int link_carrier_gained(Link *link) { link_enter_failed(link); return r; } + + r = link_enter_set_addresses(link); + if (r < 0) + return r; } r = link_handle_bound_by_list(link); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 1da99cd5bc..654d6a0316 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -28,8 +28,6 @@ Match.Architecture, config_parse_net_condition, Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) Link.IAID, config_parse_iaid, 0, offsetof(Network, iaid) -DUID.Type, config_parse_duid_type, 0, offsetof(Network, duid_type) -DUID.RawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_NETWORK, offsetof(Network, dhcp_duid) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) @@ -85,6 +83,8 @@ DHCP.Hostname, config_parse_hostname, DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid_type) +DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_NETWORK, offsetof(Network, dhcp_duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 07f8fb028f..2ebcdfa744 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -137,7 +137,6 @@ static int network_load_one(Manager *manager, const char *filename) { r = config_parse(NULL, filename, file, "Match\0" "Link\0" - "DUID\0" "Network\0" "Address\0" "Route\0" diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index ab9b777d9a..bda2707e6d 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -68,15 +68,15 @@ int route_new_static(Network *network, unsigned section, Route **ret) { route->protocol = RTPROT_STATIC; if (section) { + route->section = section; + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); if (r < 0) return r; - - route->section = section; } - LIST_PREPEND(routes, network->static_routes, route); route->network = network; + LIST_PREPEND(routes, network->static_routes, route); *ret = route; route = NULL; diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 116655cdd2..34e1310e29 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -16,7 +16,7 @@ struct ConfigPerfItem; %includes %% Exec.Boot, config_parse_boot, 0, 0 -Exec.ProcessTwo, config_parse_pid2, 0, 0, +Exec.ProcessTwo, config_parse_pid2, 0, 0 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) @@ -26,11 +26,13 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PrivateUsers, config_parse_private_users, 0, 0 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 +Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) 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) diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c new file mode 100644 index 0000000000..c7382d412d --- /dev/null +++ b/src/nspawn/nspawn-patch-uid.c @@ -0,0 +1,469 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 <fcntl.h> +#include <linux/magic.h> +#ifdef HAVE_ACL +#include <sys/acl.h> +#endif +#include <sys/stat.h> +#include <sys/vfs.h> +#include <unistd.h> + +#include "acl-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "missing.h" +#include "nspawn-patch-uid.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" + +#ifdef HAVE_ACL + +static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + acl_t acl; + + assert(fd >= 0); + assert(ret); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + acl = acl_get_file(procfs_path, type); + } else if (type == ACL_TYPE_ACCESS) + acl = acl_get_fd(fd); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + acl = acl_get_file(procfs_path, type); + } + if (!acl) + return -errno; + + *ret = acl; + return 0; +} + +static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + assert(fd >= 0); + assert(acl); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + r = acl_set_file(procfs_path, type, acl); + } else if (type == ACL_TYPE_ACCESS) + r = acl_set_fd(fd, acl); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + r = acl_set_file(procfs_path, type, acl); + } + if (r < 0) + return -errno; + + return 0; +} + +static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) { + _cleanup_(acl_freep) acl_t copy = NULL; + acl_entry_t i; + int r; + + assert(acl); + assert(ret); + + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + while (r > 0) { + uid_t *old_uid, new_uid; + bool modify = false; + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (IN_SET(tag, ACL_USER, ACL_GROUP)) { + + /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that + * this is actually OK */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + old_uid = acl_get_qualifier(i); + if (!old_uid) + return -errno; + + new_uid = shift | (*old_uid & UINT32_C(0xFFFF)); + if (!uid_is_valid(new_uid)) + return -EINVAL; + + modify = new_uid != *old_uid; + if (modify && !copy) { + int n; + + /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the + * beginning, so that we copy all entries, starting from the first, this time. */ + + n = acl_entries(acl); + if (n < 0) + return -errno; + + copy = acl_init(n); + if (!copy) + return -errno; + + /* Seek back to the beginning */ + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + continue; + } + } + + if (copy) { + acl_entry_t new_entry; + + if (acl_create_entry(©, &new_entry) < 0) + return -errno; + + if (acl_copy_entry(new_entry, i) < 0) + return -errno; + + if (modify) + if (acl_set_qualifier(new_entry, &new_uid) < 0) + return -errno; + } + + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i); + if (r < 0) + return -errno; + } + + *ret = copy; + copy = NULL; + + return !!*ret; +} + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + /* ACLs are not supported on symlinks, there's no point in trying */ + if (S_ISLNK(st->st_mode)) + return 0; + + r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl); + if (r == -EOPNOTSUPP) + return 0; + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted); + if (r < 0) + return r; + + changed = true; + } + + if (S_ISDIR(st->st_mode)) { + acl_free(acl); + acl_free(shifted); + + acl = shifted = NULL; + + r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl); + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted); + if (r < 0) + return r; + + changed = true; + } + } + + return changed; +} + +#else + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + return 0; +} + +#endif + +static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) { + uid_t new_uid; + gid_t new_gid; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + new_uid = shift | (st->st_uid & UINT32_C(0xFFFF)); + new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF)); + + if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid)) + return -EINVAL; + + if (st->st_uid != new_uid || st->st_gid != new_gid) { + if (name) + r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW); + else + r = fchown(fd, new_uid, new_gid); + if (r < 0) + return -errno; + + /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */ + if (name && !S_ISLNK(st->st_mode)) + r = fchmodat(fd, name, st->st_mode, 0); + else + r = fchmod(fd, st->st_mode); + if (r < 0) + return -errno; + + changed = true; + } + + r = patch_acls(fd, name, st, shift); + if (r < 0) + return r; + + return r > 0 || changed; +} + +static int is_procfs_sysfs_or_suchlike(int fd) { + struct statfs sfs; + + assert(fd >= 0); + + if (fstatfs(fd, &sfs) < 0) + return -errno; + + return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC); +} + +static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) { + bool changed = false; + int r; + + assert(fd >= 0); + + /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we + * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's + * stop the recursion when we hit a procfs or sysfs file system. */ + r = is_procfs_sysfs_or_suchlike(fd); + if (r < 0) + goto finish; + if (r > 0) { + r = 0; /* don't recurse */ + goto finish; + } + + r = patch_fd(fd, NULL, st, shift); + if (r == -EROFS) { + _cleanup_free_ char *name = NULL; + + if (!is_toplevel) { + /* When we hit a ready-only subtree we simply skip it, but log about it. */ + (void) fd_get_path(fd, &name); + log_debug("Skippping read-only file or directory %s.", strna(name)); + r = 0; + } + + goto finish; + } + if (r < 0) + goto finish; + + if (S_ISDIR(st->st_mode)) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + if (!donate_fd) { + int copy; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) { + r = -errno; + goto finish; + } + + fd = copy; + donate_fd = true; + } + + d = fdopendir(fd); + if (!d) { + r = -errno; + goto finish; + } + fd = -1; + + FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { + struct stat fst; + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { + r = -errno; + goto finish; + } + + if (S_ISDIR(fst.st_mode)) { + int subdir_fd; + + subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (subdir_fd < 0) { + r = -errno; + goto finish; + + } + + r = recurse_fd(subdir_fd, true, &fst, shift, false); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else { + r = patch_fd(dirfd(d), de->d_name, &fst, shift); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + } + } + } + + r = changed; + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) { + struct stat st; + int r; + + assert(fd >= 0); + + /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an + * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges + * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual + * UID within the container. */ + + if ((shift & 0xFFFF) != 0) { + /* We only support containers where the shift starts at a 2^16 boundary */ + r = -EOPNOTSUPP; + goto finish; + } + + if (range != 0x10000) { + /* We only support containers with 16bit UID ranges for the patching logic */ + r = -EOPNOTSUPP; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) { + /* We only support containers where the uid/gid container ID match */ + r = -EBADE; + goto finish; + } + + /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume + * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */ + if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0) + return 0; + + return recurse_fd(fd, donate_fd, &st, shift, true); + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +int fd_patch_uid(int fd, uid_t shift, uid_t range) { + return fd_patch_uid_internal(fd, false, shift, range); +} + +int path_patch_uid(const char *path, uid_t shift, uid_t range) { + int fd; + + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) + return -errno; + + return fd_patch_uid_internal(fd, true, shift, range); +} diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h new file mode 100644 index 0000000000..55d0990016 --- /dev/null +++ b/src/nspawn/nspawn-patch-uid.h @@ -0,0 +1,23 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 <sys/types.h> + +int fd_patch_uid(int fd, uid_t shift, uid_t range); +int path_patch_uid(const char *path, uid_t shift, uid_t range); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 4fb0054698..b98a79fd09 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -25,7 +25,9 @@ #include "parse-util.h" #include "process-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" +#include "string-util.h" int settings_load(FILE *f, const char *path, Settings **ret) { _cleanup_(settings_freep) Settings *s = NULL; @@ -40,9 +42,13 @@ int settings_load(FILE *f, const char *path, Settings **ret) { s->start_mode = _START_MODE_INVALID; s->personality = PERSONALITY_INVALID; + s->userns_mode = _USER_NAMESPACE_MODE_INVALID; + s->uid_shift = UID_INVALID; + s->uid_range = UID_INVALID; s->read_only = -1; s->volatile_mode = _VOLATILE_MODE_INVALID; + s->userns_chown = -1; s->private_network = -1; s->network_veth = -1; @@ -59,6 +65,16 @@ int settings_load(FILE *f, const char *path, Settings **ret) { if (r < 0) return r; + /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either + * both fields shall be initialized or neither. */ + if (s->userns_mode == USER_NAMESPACE_PICK) + s->userns_chown = true; + else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) + s->userns_chown = false; + + if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID) + s->userns_mode = USER_NAMESPACE_NO; + *ret = s; s = NULL; @@ -392,3 +408,73 @@ conflict: log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); return 0; } + +int config_parse_private_users( + 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 = parse_boolean(rvalue); + if (r == 0) { + /* no: User namespacing off */ + settings->userns_mode = USER_NAMESPACE_NO; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (streq(rvalue, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + settings->userns_mode = USER_NAMESPACE_PICK; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else { + const char *range, *shift; + uid_t sh, rn; + + /* anything else: User namespacing on, UID range is explicitly configured */ + + range = strchr(rvalue, ':'); + if (range) { + shift = strndupa(rvalue, range - rvalue); + range++; + + r = safe_atou32(range, &rn); + if (r < 0 || rn <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range); + return 0; + } + } else { + shift = rvalue; + rn = UINT32_C(0x10000); + } + + r = parse_uid(shift, &sh); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range); + return 0; + } + + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = sh; + settings->uid_range = rn; + } + + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index a017405cd9..e12e91b886 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -33,6 +33,14 @@ typedef enum StartMode { _START_MODE_INVALID = -1 } StartMode; +typedef enum UserNamespaceMode { + USER_NAMESPACE_NO, + USER_NAMESPACE_FIXED, + USER_NAMESPACE_PICK, + _USER_NAMESPACE_MODE_MAX, + _USER_NAMESPACE_MODE_INVALID = -1, +} UserNamespaceMode; + typedef enum SettingsMask { SETTING_START_MODE = 1 << 0, SETTING_ENVIRONMENT = 1 << 1, @@ -47,7 +55,8 @@ typedef enum SettingsMask { SETTING_VOLATILE_MODE = 1 << 10, SETTING_CUSTOM_MOUNTS = 1 << 11, SETTING_WORKING_DIRECTORY = 1 << 12, - _SETTINGS_MASK_ALL = (1 << 13) -1 + SETTING_USERNS = 1 << 13, + _SETTINGS_MASK_ALL = (1 << 14) -1 } SettingsMask; typedef struct Settings { @@ -62,12 +71,15 @@ typedef struct Settings { unsigned long personality; sd_id128_t machine_id; char *working_directory; + UserNamespaceMode userns_mode; + uid_t uid_shift, uid_range; /* [Image] */ int read_only; VolatileMode volatile_mode; CustomMount *custom_mounts; unsigned n_custom_mounts; + int userns_chown; /* [Network] */ int private_network; @@ -99,3 +111,4 @@ int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, co 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); int config_parse_boot(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_pid2(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_private_users(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 d687df8a09..3fc6cc955c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -22,7 +22,9 @@ #endif #include <errno.h> #include <getopt.h> +#include <grp.h> #include <linux/loop.h> +#include <pwd.h> #include <sched.h> #ifdef HAVE_SECCOMP #include <seccomp.h> @@ -75,6 +77,7 @@ #include "nspawn-expose-ports.h" #include "nspawn-mount.h" #include "nspawn-network.h" +#include "nspawn-patch-uid.h" #include "nspawn-register.h" #include "nspawn-settings.h" #include "nspawn-setuid.h" @@ -101,6 +104,11 @@ #include "user-util.h" #include "util.h" +/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit + * UID range here */ +#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) +#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) + typedef enum ContainerStatus { CONTAINER_TERMINATED, CONTAINER_REBOOTED @@ -173,8 +181,9 @@ static char *arg_image = NULL; static VolatileMode arg_volatile_mode = VOLATILE_NO; static ExposePort *arg_expose_ports = NULL; static char **arg_property = NULL; +static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; -static bool arg_userns = false; +static bool arg_userns_chown = false; static int arg_kill_signal = 0; static bool arg_unified_cgroup_hierarchy = false; static SettingsMask arg_settings_mask = 0; @@ -202,8 +211,10 @@ static void help(void) { " --uuid=UUID Set a specific machine UUID for the container\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" + " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" " --private-users[=UIDBASE[:NUIDS]]\n" - " Run within user namespace\n" + " Run within user namespace, user configured UID/GID range\n" + " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" " --private-network Disable network in container\n" " --network-interface=INTERFACE\n" " Assign an existing network interface to the\n" @@ -272,9 +283,15 @@ static int custom_mounts_prepare(void) { for (i = 0; i < arg_n_custom_mounts; i++) { CustomMount *m = &arg_custom_mounts[i]; - if (arg_userns && arg_uid_shift == UID_INVALID && path_equal(m->destination, "/")) { - log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); - return -EINVAL; + if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) { + + if (arg_userns_chown) { + log_error("--private-users-chown may not be combined with custom root mounts."); + return -EINVAL; + } else if (arg_uid_shift == UID_INVALID) { + log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); + return -EINVAL; + } } if (m->type != CUSTOM_MOUNT_OVERLAY) @@ -349,6 +366,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_KILL_SIGNAL, ARG_SETTINGS, ARG_CHDIR, + ARG_PRIVATE_USERS_CHOWN, }; static const struct option options[] = { @@ -392,6 +410,7 @@ static int parse_argv(int argc, char *argv[]) { { "port", required_argument, NULL, 'p' }, { "property", required_argument, NULL, ARG_PROPERTY }, { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, + { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN}, { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, { "settings", required_argument, NULL, ARG_SETTINGS }, { "chdir", required_argument, NULL, ARG_CHDIR }, @@ -406,7 +425,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0) switch (c) { @@ -797,10 +816,29 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PRIVATE_USERS: - if (optarg) { + + r = optarg ? parse_boolean(optarg) : 1; + if (r == 0) { + /* no: User namespacing off */ + arg_userns_mode = USER_NAMESPACE_NO; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + arg_userns_mode = USER_NAMESPACE_FIXED; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (streq(optarg, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else { _cleanup_free_ char *buffer = NULL; const char *range, *shift; + /* anything else: User namespacing on, UID range is explicitly configured */ + range = strchr(optarg, ':'); if (range) { buffer = strndup(optarg, range - optarg); @@ -820,9 +858,28 @@ static int parse_argv(int argc, char *argv[]) { log_error("Failed to parse UID: %s", optarg); return -EINVAL; } + + arg_userns_mode = USER_NAMESPACE_FIXED; + } + + arg_settings_mask |= SETTING_USERNS; + break; + + case 'U': + if (userns_supported()) { + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + + arg_settings_mask |= SETTING_USERNS; } - arg_userns = true; + break; + + case ARG_PRIVATE_USERS_CHOWN: + arg_userns_chown = true; + + arg_settings_mask |= SETTING_USERNS; break; case ARG_KILL_SIGNAL: @@ -893,6 +950,9 @@ static int parse_argv(int argc, char *argv[]) { if (arg_share_system) arg_register = false; + if (arg_userns_mode == USER_NAMESPACE_PICK) + arg_userns_chown = true; + if (arg_start_mode != START_PID1 && arg_share_system) { log_error("--boot and --share-system may not be combined."); return -EINVAL; @@ -933,8 +993,15 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_userns && access("/proc/self/uid_map", F_OK) < 0) - return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support."); + if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) { + log_error("--private-users= is not supported, kernel compiled without user namespace support."); + return -EOPNOTSUPP; + } + + if (arg_userns_chown && arg_read_only) { + log_error("--read-only and --private-users-chown may not be combined."); + return -EINVAL; + } if (argc > optind) { arg_parameters = strv_copy(argv + optind); @@ -993,7 +1060,7 @@ static int verify_arguments(void) { static int userns_lchown(const char *p, uid_t uid, gid_t gid) { assert(p); - if (!arg_userns) + if (arg_userns_mode == USER_NAMESPACE_NO) return 0; if (uid == UID_INVALID && gid == GID_INVALID) @@ -1397,7 +1464,7 @@ static int setup_journal(const char *directory) { if (sd_id128_equal(arg_uuid, this_id)) { log_full(try ? LOG_WARNING : LOG_ERR, - "Host and machine ids are equal (%s): refusing to link journals", id); + "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id)); if (try) return 0; return -EEXIST; @@ -2218,6 +2285,29 @@ static int setup_machine_id(const char *directory) { return 0; } +static int recursive_chown(const char *directory, uid_t shift, uid_t range) { + int r; + + assert(directory); + + if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown) + return 0; + + r = path_patch_uid(directory, arg_uid_shift, arg_uid_range); + if (r == -EOPNOTSUPP) + return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16."); + if (r == -EBADE) + return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match."); + if (r < 0) + return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m"); + if (r == 0) + log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation."); + else + log_debug("Patched directory tree to match UID/GID range."); + + return r; +} + static int mount_devices( const char *where, const char *root_device, bool root_device_rw, @@ -2435,7 +2525,7 @@ static int determine_names(void) { static int determine_uid_shift(const char *directory) { int r; - if (!arg_userns) { + if (arg_userns_mode == USER_NAMESPACE_NO) { arg_uid_shift = 0; return 0; } @@ -2462,7 +2552,6 @@ static int determine_uid_shift(const char *directory) { return -EINVAL; } - log_info("Using user namespaces with base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); return 0; } @@ -2499,7 +2588,7 @@ static int inner_child( cg_unified_flush(); - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { /* Tell the parent, that it now can write the UID map. */ (void) barrier_place(barrier); /* #1 */ @@ -2510,7 +2599,14 @@ static int inner_child( } } - r = mount_all(NULL, arg_userns, true, arg_uid_shift, arg_private_network, arg_uid_range, arg_selinux_apifs_context); + r = mount_all(NULL, + arg_userns_mode != USER_NAMESPACE_NO, + true, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) return r; @@ -2749,7 +2845,8 @@ static int outer_child( if (r < 0) return r; - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* Let the parent know which UID shift we read from the image */ l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); if (l < 0) return log_error_errno(errno, "Failed to send UID shift: %m"); @@ -2757,17 +2854,49 @@ static int outer_child( log_error("Short write while sending UID shift."); return -EIO; } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift + * we just read from the image is available. If yes, it will send the UID shift back to us, if + * not it will pick a different one, and send it back to us. */ + + l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) + return log_error_errno(errno, "Failed to recv UID shift: %m"); + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while recieving UID shift."); + return -EIO; + } + } + + log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); } /* Turn directory into bind mount */ if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0) return log_error_errno(errno, "Failed to make bind mount: %m"); - r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); + r = recursive_chown(directory, arg_uid_shift, arg_uid_range); if (r < 0) return r; - r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); + r = setup_volatile( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); + if (r < 0) + return r; + + r = setup_volatile_state( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); if (r < 0) return r; @@ -2781,7 +2910,13 @@ static int outer_child( return log_error_errno(r, "Failed to make tree read-only: %m"); } - r = mount_all(directory, arg_userns, false, arg_private_network, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_all(directory, + arg_userns_mode != USER_NAMESPACE_NO, + false, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; @@ -2823,11 +2958,24 @@ static int outer_child( if (r < 0) return r; - r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_custom( + directory, + arg_custom_mounts, + arg_n_custom_mounts, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; - r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_cgroups( + directory, + arg_unified_cgroup_hierarchy, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; @@ -2838,7 +2986,7 @@ static int outer_child( pid = raw_clone(SIGCHLD|CLONE_NEWNS| (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) | (arg_private_network ? CLONE_NEWNET : 0) | - (arg_userns ? CLONE_NEWUSER : 0), + (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0), NULL); if (pid < 0) return log_error_errno(errno, "Failed to fork inner child: %m"); @@ -2882,6 +3030,61 @@ static int outer_child( return 0; } +static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { + unsigned n_tries = 100; + uid_t candidate; + int r; + + assert(shift); + assert(ret_lock_file); + assert(arg_userns_mode == USER_NAMESPACE_PICK); + assert(arg_uid_range == 0x10000U); + + candidate = *shift; + + (void) mkdir("/run/systemd/nspawn-uid", 0755); + + for (;;) { + char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT; + + if (--n_tries <= 0) + return -EBUSY; + + if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX) + goto next; + if ((candidate & UINT32_C(0xFFFF)) != 0) + goto next; + + xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate); + r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf); + if (r == -EBUSY) /* Range already taken by another nspawn instance */ + goto next; + if (r < 0) + return r; + + /* Make some superficial checks whether the range is currently known in the user database */ + if (getpwuid(candidate)) + goto next; + if (getpwuid(candidate + UINT32_C(0xFFFE))) + goto next; + if (getgrgid(candidate)) + goto next; + if (getgrgid(candidate + UINT32_C(0xFFFE))) + goto next; + + *ret_lock_file = lf; + lf = (struct LockFile) LOCK_FILE_INIT; + *shift = candidate; + return 0; + + next: + random_bytes(&candidate, sizeof(candidate)); + candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN; + candidate &= (uid_t) UINT32_C(0xFFFF0000); + } +} + static int setup_uid_map(pid_t pid) { char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1]; int r; @@ -3113,6 +3316,19 @@ static int load_settings(void) { } } + if ((arg_settings_mask & SETTING_USERNS) == 0 && + settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { + + if (!arg_settings_trusted) + log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p); + else { + arg_userns_mode = settings->userns_mode; + arg_uid_shift = settings->uid_shift; + arg_uid_range = settings->uid_range; + arg_userns_chown = settings->userns_chown; + } + } + return 0; } @@ -3123,7 +3339,7 @@ int main(int argc, char *argv[]) { _cleanup_close_ int master = -1, image_fd = -1; _cleanup_fdset_free_ FDSet *fds = NULL; int r, n_fd_passed, loop_nr = -1; - char veth_name[IFNAMSIZ]; + char veth_name[IFNAMSIZ] = ""; bool secondary = false, remove_subvol = false; sigset_t mask_chld; pid_t pid = 0; @@ -3351,20 +3567,42 @@ int main(int argc, char *argv[]) { } for (;;) { - _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, - pid_socket_pair[2] = { -1, -1 }, uuid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 }; - ContainerStatus container_status; - _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; static const struct sigaction sa = { .sa_handler = nop_signal_handler, .sa_flags = SA_NOCLDSTOP, }; - int ifi = 0; - ssize_t l; + + _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; + _cleanup_close_ int etc_passwd_lock = -1; + _cleanup_close_pair_ int + kmsg_socket_pair[2] = { -1, -1 }, + rtnl_socket_pair[2] = { -1, -1 }, + pid_socket_pair[2] = { -1, -1 }, + uuid_socket_pair[2] = { -1, -1 }, + uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + ContainerStatus container_status; char last_char = 0; + int ifi = 0; + ssize_t l; + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely + * check with getpwuid() if the specific user already exists. Note that /etc might be + * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we + * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are + * really just an extra safety net. We kinda assume that the UID range we allocate from is + * really ours. */ + + etc_passwd_lock = take_etc_passwd_lock(NULL); + if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) { + log_error_errno(r, "Failed to take /etc/passwd lock: %m"); + goto finish; + } + } r = barrier_create(&barrier); if (r < 0) { @@ -3392,7 +3630,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_userns) + if (arg_userns_mode != USER_NAMESPACE_NO) if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); goto finish; @@ -3468,6 +3706,43 @@ int main(int argc, char *argv[]) { uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* The child just let us know the UID shift it might have read from the image. */ + l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while reading UID shift."); + r = EIO; + goto finish; + } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* If we are supposed to pick the UID shift, let's try to use the shift read from the + * image, but if that's already in use, pick a new one, and report back to the child, + * which one we now picked. */ + + r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); + if (r < 0) { + log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); + goto finish; + } + + l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); + if (l < 0) { + r = log_error_errno(errno, "Failed to send UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short write while writing UID shift."); + r = -EIO; + goto finish; + } + } + } + /* Wait for the outer child. */ r = wait_for_terminate_and_warn("namespace helper", pid, NULL); if (r < 0) @@ -3504,24 +3779,13 @@ int main(int argc, char *argv[]) { log_debug("Init process invoked as PID " PID_FMT, pid); - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { if (!barrier_place_and_sync(&barrier)) { /* #1 */ log_error("Child died too early."); r = -ESRCH; goto finish; } - l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short read while reading UID shift."); - r = EIO; - goto finish; - } - r = setup_uid_map(pid); if (r < 0) goto finish; @@ -3619,6 +3883,10 @@ int main(int argc, char *argv[]) { goto finish; } + /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear + * in getpwuid(), thus we can release the /etc/passwd lock. */ + etc_passwd_lock = safe_close(etc_passwd_lock); + sd_notifyf(false, "READY=1\n" "STATUS=Container running.\n" diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c new file mode 100644 index 0000000000..11c5321788 --- /dev/null +++ b/src/nspawn/test-patch-uid.c @@ -0,0 +1,61 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 <stdlib.h> + +#include "log.h" +#include "nspawn-patch-uid.h" +#include "user-util.h" +#include "util.h" + +int main(int argc, char *argv[]) { + uid_t shift, range; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc != 4) { + log_error("Expected PATH SHIFT RANGE parameters."); + return EXIT_FAILURE; + } + + r = parse_uid(argv[2], &shift); + if (r < 0) { + log_error_errno(r, "Failed to parse UID shift %s.", argv[2]); + return EXIT_FAILURE; + } + + r = parse_gid(argv[3], &range); + if (r < 0) { + log_error_errno(r, "Failed to parse UID range %s.", argv[3]); + return EXIT_FAILURE; + } + + r = path_patch_uid(argv[1], shift, range); + if (r < 0) { + log_error_errno(r, "Failed to patch directory tree: %m"); + return EXIT_FAILURE; + } + + log_info("Changed: %s", yes_no(r)); + + return EXIT_SUCCESS; +} diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index da479aec8d..2b755cea28 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -684,7 +684,7 @@ static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { static const struct { const char *result, *explanation; } explanations [] = { - { "resources", "a configured resource limit was exceeded" }, + { "resources", "of unavailable resources or another system error" }, { "timeout", "a timeout was exceeded" }, { "exit-code", "the control process exited with error code" }, { "signal", "a fatal signal was delivered to the control process" }, diff --git a/src/shared/dropin.c b/src/shared/dropin.c index cc1acd6f23..b9cd952ac8 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -160,7 +160,7 @@ static int iterate_dir( if (!de) break; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; f = strjoin(path, "/", de->d_name, NULL); diff --git a/src/shared/install.c b/src/shared/install.c index b74ff6de22..f02d81504f 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -68,6 +68,25 @@ typedef struct { static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); +bool unit_type_may_alias(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_TIMER, + UNIT_PATH); +} + +bool unit_type_may_template(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_TIMER, + UNIT_PATH); +} + static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -898,6 +917,36 @@ fail: return r; } +static int config_parse_alias( + 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) { + + const char *name; + UnitType type; + + assert(filename); + assert(lvalue); + assert(rvalue); + + name = basename(filename); + type = unit_name_to_type(name); + if (!unit_type_may_alias(type)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "Aliases are not allowed for %s units, ignoring.", + unit_type_to_string(type)); + + return config_parse_strv(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); +} + static int config_parse_also( const char *unit, const char *filename, @@ -954,6 +1003,7 @@ static int config_parse_default_instance( void *userdata) { UnitFileInstallInfo *i = data; + const char *name; char *printed; int r; @@ -961,6 +1011,15 @@ static int config_parse_default_instance( assert(lvalue); assert(rvalue); + name = basename(filename); + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) + /* When enabling an instance, we might be using a template unit file, + * but we should ignore DefaultInstance silently. */ + return 0; + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "DefaultInstance only makes sense for template units, ignoring."); + r = install_full_printf(i, rvalue, &printed); if (r < 0) return r; @@ -983,7 +1042,7 @@ static int unit_file_load( SearchFlags flags) { const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "Alias", config_parse_alias, 0, &info->aliases }, { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, @@ -991,6 +1050,8 @@ static int unit_file_load( {} }; + const char *name; + UnitType type; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; struct stat st; @@ -1000,6 +1061,12 @@ static int unit_file_load( assert(info); assert(path); + name = basename(path); + type = unit_name_to_type(name); + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && + !unit_type_may_template(type)) + return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); + if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); if (r < 0) @@ -2653,7 +2720,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( UnitFileScope scope, const char *root_dir, - Hashmap *h) { + Hashmap *h, + char **states, + char **patterns) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; char **i; @@ -2685,6 +2754,9 @@ int unit_file_get_list( if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; + if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE)) + continue; + if (hashmap_get(h, de->d_name)) continue; @@ -2705,6 +2777,10 @@ int unit_file_get_list( if (r < 0) f->state = UNIT_FILE_BAD; + if (!strv_isempty(states) && + !strv_contains(states, unit_file_state_to_string(f->state))) + continue; + r = hashmap_put(h, basename(f->path), f); if (r < 0) return r; diff --git a/src/shared/install.h b/src/shared/install.h index 4133faffa2..5812447c5b 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -138,6 +138,9 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { return !strv_isempty(i->also); } +bool unit_type_may_alias(UnitType type) _const_; +bool unit_type_may_template(UnitType type) _const_; + int unit_file_enable( UnitFileScope scope, bool runtime, @@ -232,7 +235,7 @@ int unit_file_add_dependency( int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); +int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); Hashmap* unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index cd3d53669c..9351b85eed 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -287,7 +287,10 @@ static int output_short( if (r < 0) return r; } - + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } if (r < 0) return log_error_errno(r, "Failed to get journal fields: %m"); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index bebfc40efe..66f58ecd92 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -423,7 +423,7 @@ int image_remove(Image *i) { case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); if (r < 0) return r; @@ -505,7 +505,7 @@ int image_rename(Image *i, const char *new_name) { (void) read_attr_path(i->path, &file_attr); if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); /* fall through */ @@ -538,7 +538,7 @@ int image_rename(Image *i, const char *new_name) { /* Restore the immutable bit, if it was set before */ if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(new_path, true, FS_IMMUTABLE_FL); + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); free(i->path); i->path = new_path; @@ -603,13 +603,21 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_SUBVOLUME: case IMAGE_DIRECTORY: + /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain + * directory.*/ + new_path = strjoina("/var/lib/machines/", new_name); r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + if (r == -EOPNOTSUPP) { + /* No btrfs snapshots supported, create a normal directory then. */ - /* Enable "subtree" quotas for the copy, if we didn't - * copy any quota from the source. */ - (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + r = copy_directory(i->path, new_path, false); + if (r >= 0) + (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); + } else if (r >= 0) + /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(new_path, 0, true); break; @@ -670,7 +678,7 @@ int image_read_only(Image *i, bool b) { a read-only subvolume, but at least something, and we can read the value back.*/ - r = chattr_path(i->path, b, FS_IMMUTABLE_FL); + r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); if (r < 0) return r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 595d6853c6..9af25e22a4 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -541,6 +541,7 @@ static int get_unit_list( size_t size = c; int r; UnitInfo u; + bool fallback = false; assert(bus); assert(unit_infos); @@ -552,8 +553,7 @@ static int get_unit_list( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitsFiltered"); - + "ListUnitsByPatterns"); if (r < 0) return bus_log_create_error(r); @@ -561,7 +561,34 @@ static int get_unit_list( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_append_strv(m, patterns); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + /* Fallback to legacy ListUnitsFiltered method */ + fallback = true; + log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitsFiltered"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } if (r < 0) return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); @@ -572,7 +599,7 @@ static int get_unit_list( while ((r = bus_parse_unit_info(reply, &u)) > 0) { u.machine = machine; - if (!output_show_unit(&u, patterns)) + if (!output_show_unit(&u, fallback ? patterns : NULL)) continue; if (!GREEDY_REALLOC(*unit_infos, size, c+1)) @@ -1282,7 +1309,7 @@ static int compare_unit_file_list(const void *a, const void *b) { return strcasecmp(basename(u->path), basename(v->path)); } -static bool output_show_unit_file(const UnitFileList *u, char **patterns) { +static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) { assert(u); if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE)) @@ -1299,8 +1326,8 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) { return false; } - if (!strv_isempty(arg_states) && - !strv_find(arg_states, unit_file_state_to_string(u->state))) + if (!strv_isempty(states) && + !strv_find(states, unit_file_state_to_string(u->state))) return false; return true; @@ -1373,6 +1400,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { const char *state; char *path; int r; + bool fallback = false; pager_open(arg_no_pager, false); @@ -1386,7 +1414,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (!h) return log_oom(); - r = unit_file_get_list(arg_scope, arg_root, h); + r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1)); if (r < 0) { unit_file_list_free(h); return log_error_errno(r, "Failed to get unit file list: %m"); @@ -1401,7 +1429,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { } HASHMAP_FOREACH(u, h, i) { - if (!output_show_unit_file(u, strv_skip(argv, 1))) + if (!output_show_unit_file(u, NULL, NULL)) continue; units[c++] = *u; @@ -1411,6 +1439,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { assert(c <= n_units); hashmap_free(h); } else { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; @@ -1418,15 +1447,44 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitFiles", - &error, - &reply, - NULL); + "ListUnitFilesByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + /* Fallback to legacy ListUnitFiles method */ + fallback = true; + log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitFiles"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } if (r < 0) return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r)); @@ -1444,7 +1502,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { unit_file_state_from_string(state) }; - if (output_show_unit_file(&units[c], strv_skip(argv, 1))) + if (output_show_unit_file(&units[c], + fallback ? arg_states : NULL, + fallback ? strv_skip(argv, 1) : NULL)) c++; } diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index d4c6f409cd..9c36b27157 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -67,10 +67,11 @@ typedef struct sd_journal sd_journal; /* Open flags */ enum { - SD_JOURNAL_LOCAL_ONLY = 1, - SD_JOURNAL_RUNTIME_ONLY = 2, - SD_JOURNAL_SYSTEM = 4, - SD_JOURNAL_CURRENT_USER = 8, + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ }; @@ -84,8 +85,10 @@ enum { int sd_journal_open(sd_journal **ret, int flags); int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); diff --git a/src/test/test-copy.c b/src/test/test-copy.c index cb437754b4..68154fc4e8 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -95,6 +95,8 @@ static void test_copy_tree(void) { char **links = STRV_MAKE("link", "file", "link2", "dir1/file"); char **p, **link; + const char *unixsockp; + struct stat st; log_info("%s", __func__); @@ -102,26 +104,34 @@ static void test_copy_tree(void) { (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL); STRV_FOREACH(p, files) { - char *f = strjoina(original_dir, *p); + _cleanup_free_ char *f; + + assert_se(f = strappend(original_dir, *p)); assert_se(mkdir_parents(f, 0755) >= 0); assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0); } STRV_FOREACH_PAIR(link, p, links) { - char *f = strjoina(original_dir, *p); - char *l = strjoina(original_dir, *link); + _cleanup_free_ char *f, *l; + + assert_se(f = strappend(original_dir, *p)); + assert_se(l = strappend(original_dir, *link)); assert_se(mkdir_parents(l, 0755) >= 0); assert_se(symlink(f, l) == 0); } + unixsockp = strjoina(original_dir, "unixsock"); + assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0); + assert_se(copy_tree(original_dir, copy_dir, true) == 0); STRV_FOREACH(p, files) { - _cleanup_free_ char *buf = NULL; + _cleanup_free_ char *buf = NULL, *f; size_t sz = 0; - char *f = strjoina(copy_dir, *p); + + assert_se(f = strappend(copy_dir, *p)); assert_se(access(f, F_OK) == 0); assert_se(read_full_file(f, &buf, &sz) == 0); @@ -129,14 +139,19 @@ static void test_copy_tree(void) { } STRV_FOREACH_PAIR(link, p, links) { - _cleanup_free_ char *target = NULL; - char *f = strjoina(original_dir, *p); - char *l = strjoina(copy_dir, *link); + _cleanup_free_ char *target = NULL, *f, *l; + + assert_se(f = strjoin(original_dir, *p, NULL)); + assert_se(l = strjoin(copy_dir, *link, NULL)); assert_se(readlink_and_canonicalize(l, &target) == 0); assert_se(path_equal(f, target)); } + unixsockp = strjoina(copy_dir, "unixsock"); + assert_se(stat(unixsockp, &st) >= 0); + assert_se(S_ISSOCK(st.st_mode)); + assert_se(copy_tree(original_dir, copy_dir, false) < 0); assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 2d73c9743b..4680b0336d 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -606,7 +606,7 @@ static void test_preset_and_list(const char *root) { 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); + assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); diff --git a/src/test/test-install.c b/src/test/test-install.c index 50315c1d9a..0ac85f040a 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -50,7 +50,7 @@ int main(int argc, char* argv[]) { log_parse_environment(); h = hashmap_new(&string_hash_ops); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); assert_se(r == 0); HASHMAP_FOREACH(p, h, i) { diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 5d77e2959c..b53324b5e6 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -489,6 +489,27 @@ static void test_filename_is_valid(void) { assert_se(filename_is_valid("o.o")); } +static void test_hidden_or_backup_file(void) { + assert_se(hidden_or_backup_file(".hidden")); + assert_se(hidden_or_backup_file("..hidden")); + assert_se(!hidden_or_backup_file("hidden.")); + + assert_se(hidden_or_backup_file("backup~")); + assert_se(hidden_or_backup_file(".backup~")); + + assert_se(hidden_or_backup_file("lost+found")); + assert_se(hidden_or_backup_file("aquota.user")); + assert_se(hidden_or_backup_file("aquota.group")); + + assert_se(hidden_or_backup_file("test.rpmnew")); + assert_se(hidden_or_backup_file("test.dpkg-old")); + assert_se(hidden_or_backup_file("test.dpkg-remove")); + assert_se(hidden_or_backup_file("test.swp")); + + assert_se(!hidden_or_backup_file("test.rpmnew.")); + assert_se(!hidden_or_backup_file("test.dpkg-old.foo")); +} + int main(int argc, char **argv) { test_path(); test_find_binary(argv[0]); @@ -502,6 +523,7 @@ int main(int argc, char **argv) { test_path_is_mount_point(); test_file_in_same_dir(); test_filename_is_valid(); + test_hidden_or_backup_file(); return 0; } diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c index d9ac9368cd..62afd2de5e 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -99,6 +99,18 @@ int main(int argc, char *argv[]) { test_rlimit_parse_format(RLIMIT_NOFILE, "", 0, 0, -EINVAL, NULL); test_rlimit_parse_format(RLIMIT_NOFILE, "5:4", 0, 0, -EILSEQ, NULL); test_rlimit_parse_format(RLIMIT_NOFILE, "5:4:3", 0, 0, -EINVAL, NULL); + test_rlimit_parse_format(RLIMIT_NICE, "20", 20, 20, 0, "20"); + test_rlimit_parse_format(RLIMIT_NICE, "40", 40, 40, 0, "40"); + test_rlimit_parse_format(RLIMIT_NICE, "41", 41, 41, -ERANGE, "41"); + test_rlimit_parse_format(RLIMIT_NICE, "0", 0, 0, 0, "0"); + test_rlimit_parse_format(RLIMIT_NICE, "-7", 27, 27, 0, "27"); + test_rlimit_parse_format(RLIMIT_NICE, "-20", 40, 40, 0, "40"); + test_rlimit_parse_format(RLIMIT_NICE, "-21", 41, 41, -ERANGE, "41"); + test_rlimit_parse_format(RLIMIT_NICE, "-0", 20, 20, 0, "20"); + test_rlimit_parse_format(RLIMIT_NICE, "+7", 13, 13, 0, "13"); + test_rlimit_parse_format(RLIMIT_NICE, "+19", 1, 1, 0, "1"); + test_rlimit_parse_format(RLIMIT_NICE, "+20", 0, 0, -ERANGE, "0"); + test_rlimit_parse_format(RLIMIT_NICE, "+0", 20, 20, 0, "20"); return 0; } diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 114ddf8478..c340673c6c 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -53,7 +53,7 @@ static int test_unit_file_get_set(void) { h = hashmap_new(&string_hash_ops); assert_se(h); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); if (r == -EPERM || r == -EACCES) { printf("Skipping test: unit_file_get_list: %s", strerror(-r)); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 7b67831e54..c7ded451a2 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -481,7 +481,7 @@ static int show_passwords(void) { if (de->d_type != DT_REG) continue; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; if (!startswith(de->d_name, "ask.")) diff --git a/test/TEST-06-SELINUX/Makefile b/test/TEST-06-SELINUX/Makefile index 5e89a29eff..e9f93b1104 100644..120000 --- a/test/TEST-06-SELINUX/Makefile +++ b/test/TEST-06-SELINUX/Makefile @@ -1,10 +1 @@ -all: - @make -s --no-print-directory -C ../.. all - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --all -setup: - @make --no-print-directory -C ../.. all - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --setup -clean: - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean -run: - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --run +../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-08-ISSUE-2730/Makefile b/test/TEST-08-ISSUE-2730/Makefile index 5e89a29eff..e9f93b1104 100644..120000 --- a/test/TEST-08-ISSUE-2730/Makefile +++ b/test/TEST-08-ISSUE-2730/Makefile @@ -1,10 +1 @@ -all: - @make -s --no-print-directory -C ../.. all - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --all -setup: - @make --no-print-directory -C ../.. all - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --setup -clean: - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean -run: - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --run +../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-10-ISSUE-2467/Makefile b/test/TEST-10-ISSUE-2467/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-10-ISSUE-2467/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh new file mode 100755 index 0000000000..a652b0d812 --- /dev/null +++ b/test/TEST-10-ISSUE-2467/test.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467" + +. $TEST_BASE_DIR/test-functions +SKIP_INITRD=yes + +check_result_qemu() { + ret=1 + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + [[ -e $TESTDIR/root/testok ]] && ret=0 + [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR + cp -a $TESTDIR/root/var/log/journal $TESTDIR + umount $TESTDIR/root + [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed + ls -l $TESTDIR/journal/*/*.journal + test -s $TESTDIR/failed && ret=$(($ret+1)) + return $ret +} + +test_run() { + run_qemu || return 1 + check_result_qemu || return 1 + return 0 +} + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + # Create what will eventually be our root filesystem onto an overlay + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + dracut_install nc true rm + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service <<'EOF' +[Unit] +Description=Testsuite service +After=multi-user.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; echo a | nc -U /run/test.ctl; >/testok' +TimeoutStartSec=10s +EOF + + cat >$initdir/etc/systemd/system/test.socket <<'EOF' +[Socket] +ListenStream=/run/test.ctl +EOF + + cat > $initdir/etc/systemd/system/test.service <<'EOF' +[Unit] +Requires=test.socket +ConditionPathExistsGlob=/tmp/nonexistent + +[Service] +ExecStart=/bin/true +EOF + + setup_testsuite + ) || return 1 + + # mask some services that we do not want to run in these tests + ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket + ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service + + ddebug "umount $TESTDIR/root" + umount $TESTDIR/root +} + +test_cleanup() { + umount $TESTDIR/root 2>/dev/null + [[ $LOOPDEV ]] && losetup -d $LOOPDEV + return 0 +} + +do_test "$@" diff --git a/test/TEST-11-ISSUE-3166/Makefile b/test/TEST-11-ISSUE-3166/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-11-ISSUE-3166/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile
\ No newline at end of file diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh new file mode 100755 index 0000000000..7913537e9b --- /dev/null +++ b/test/TEST-11-ISSUE-3166/test.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166" + +. $TEST_BASE_DIR/test-functions +SKIP_INITRD=yes + +check_result_qemu() { + ret=1 + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + [[ -e $TESTDIR/root/testok ]] && ret=0 + [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR + cp -a $TESTDIR/root/var/log/journal $TESTDIR + umount $TESTDIR/root + [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed + ls -l $TESTDIR/journal/*/*.journal + test -s $TESTDIR/failed && ret=$(($ret+1)) + return $ret +} + +test_run() { + run_qemu || return 1 + check_result_qemu || return 1 + return 0 +} + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + # Create what will eventually be our root filesystem onto an overlay + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + dracut_install false touch + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service <<EOF +[Unit] +Description=Testsuite service +After=multi-user.target + +[Service] +ExecStart=/test-fail-on-restart.sh +Type=oneshot +EOF + + cat >$initdir/etc/systemd/system/fail-on-restart.service <<EOF +[Unit] +Description=Fail on restart + +[Service] +Type=simple +ExecStart=/bin/false +Restart=always +EOF + + + cat >$initdir/test-fail-on-restart.sh <<'EOF' +#!/bin/bash -x + +systemctl start fail-on-restart.service +active_state=$(systemctl show --property ActiveState fail-on-restart.service) +while [[ "$active_state" == "ActiveState=activating" || "$active_state" == "ActiveState=active" ]]; do + sleep 1 + active_state=$(systemctl show --property ActiveState fail-on-restart.service) +done +systemctl is-failed fail-on-restart.service || exit 1 +touch /testok +EOF + + chmod 0755 $initdir/test-fail-on-restart.sh + setup_testsuite + ) || return 1 + + ddebug "umount $TESTDIR/root" + umount $TESTDIR/root +} + +test_cleanup() { + umount $TESTDIR/root 2>/dev/null + [[ $LOOPDEV ]] && losetup -d $LOOPDEV + return 0 +} + +do_test "$@" diff --git a/test/networkd-test.py b/test/networkd-test.py index d76ab507d2..d4de5adf1a 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -5,7 +5,12 @@ # does not write anything on disk or change any system configuration; # but it assumes (and checks at the beginning) that networkd is not currently # running. -# This can be run on a normal installation, in QEMU, nspawn, or LXC. +# +# This can be run on a normal installation, in QEMU, nspawn (with +# --private-network), LXD (with "--config raw.lxc=lxc.aa_profile=unconfined"), +# or LXC system containers. You need at least the "ip" tool from the iproute +# package; it is recommended to install dnsmasq too to get full test coverage. +# # ATTENTION: This uses the *installed* networkd, not the one from the built # source tree. # diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py index aca5f1eec6..838dd57a6f 100644..100755 --- a/test/sysv-generator-test.py +++ b/test/sysv-generator-test.py @@ -1,3 +1,5 @@ +#!/usr/bin/python +# # systemd-sysv-generator integration test # # (C) 2015 Canonical Ltd. @@ -395,11 +397,12 @@ class SysvGeneratorTest(unittest.TestCase): # backup files (not enabled in rcN.d/) shutil.copy(script, script + '.bak') shutil.copy(script, script + '.old') + shutil.copy(script, script + '.tmp') + shutil.copy(script, script + '.new') err, results = self.run_generator() print(err) - self.assertEqual(sorted(results), - ['foo.bak.service', 'foo.old.service', 'foo.service']) + self.assertEqual(sorted(results), ['foo.service', 'foo.tmp.service']) # ensure we don't try to create a symlink to itself self.assertNotIn('itself', err) diff --git a/tmpfiles.d/systemd.conf.m4 b/tmpfiles.d/systemd.conf.m4 index 276bc6b1ba..150dab1e5b 100644 --- a/tmpfiles.d/systemd.conf.m4 +++ b/tmpfiles.d/systemd.conf.m4 @@ -26,32 +26,45 @@ d /run/log 0755 root root - z /run/log/journal 2755 root systemd-journal - - Z /run/log/journal/%m ~2750 root systemd-journal - - -m4_ifdef(`HAVE_ACL', +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x A+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x '',`` a+ /run/log/journal/%m - - - - d:group:adm:r-x A+ /run/log/journal/%m - - - - group:adm:r-x -''))m4_dnl +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal/%m - - - - d:group:wheel:r-x +A+ /run/log/journal/%m - - - - group:wheel:r-x +'')')')m4_dnl z /var/log/journal 2755 root systemd-journal - - z /var/log/journal/%m 2755 root systemd-journal - - z /var/log/journal/%m/system.journal 0640 root systemd-journal - - -m4_ifdef(`HAVE_ACL', +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` a+ /var/log/journal - - - - d:group:adm:r-x,d:group:wheel:r-x a+ /var/log/journal - - - - group:adm:r-x,group:wheel:r-x a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r-- -'',`` +'', `` a+ /var/log/journal - - - - d:group:adm:r-x a+ /var/log/journal - - - - group:adm:r-x a+ /var/log/journal/%m - - - - d:group:adm:r-x a+ /var/log/journal/%m - - - - group:adm:r-x a+ /var/log/journal/%m/system.journal - - - - group:adm:r-- -''))m4_dnl +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /var/log/journal - - - - d:group:wheel:r-x +a+ /var/log/journal - - - - group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:wheel:r-x +a+ /var/log/journal/%m - - - - group:wheel:r-x +a+ /var/log/journal/%m/system.journal - - - - group:wheel:r-- +'')')')m4_dnl d /var/lib/systemd 0755 root root - d /var/lib/systemd/coredump 0755 root root 3d diff --git a/units/emergency.service.in b/units/emergency.service.in index fb390eacfe..0de16f24e8 100644 --- a/units/emergency.service.in +++ b/units/emergency.service.in @@ -11,6 +11,7 @@ Documentation=man:sulogin(8) DefaultDependencies=no Conflicts=shutdown.target Conflicts=rescue.service +Conflicts=syslog.socket Before=shutdown.target [Service] diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in index 3710c595ca..685baab21d 100644 --- a/units/systemd-machined.service.in +++ b/units/systemd-machined.service.in @@ -15,7 +15,7 @@ After=machine.slice [Service] ExecStart=@rootlibexecdir@/systemd-machined BusName=org.freedesktop.machine1 -CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID +CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD WatchdogSec=3min # Note that machined cannot be placed in a mount namespace, since it diff --git a/units/systemd-nspawn@.service.in b/units/systemd-nspawn@.service.in index 1927c4d485..ea28941507 100644 --- a/units/systemd-nspawn@.service.in +++ b/units/systemd-nspawn@.service.in @@ -13,7 +13,7 @@ Before=machines.target After=network.target [Service] -ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --settings=override --machine=%i +ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i KillMode=mixed Type=notify RestartForceExitStatus=133 |