diff options
385 files changed, 8854 insertions, 2719 deletions
diff --git a/.travis.yml b/.travis.yml index 6eae0bc3e4..ab6fe298c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,13 +91,6 @@ jobs: after_script: - $CI_MANAGERS/debian.sh CLEANUP - - name: Ubuntu Xenial - language: bash - script: - - set -e - - sudo $CI_MANAGERS/xenial.sh - - set +e - - stage: Coverity language: bash env: @@ -19,6 +19,14 @@ CHANGES WITH 243 in spe: are harder to type, but we believe the change from 5 digit PIDs to 7 digit PIDs is not too hampering for usability. + * MemoryLow and MemoryMin gained hierarchy-aware counterparts, + DefaultMemoryLow and DefaultMemoryMin, which can be used to + hierarchically set default memory protection values for a particular + subtree of the unit hierarchy. + + * Memory protection directives can now take a value of zero, allowing + explicit opting out of a default value propagated by an ancestor. + … CHANGES WITH 242: @@ -753,7 +761,7 @@ CHANGES WITH 240: * Journal messages that are generated whenever a unit enters the failed state are now tagged with a unique MESSAGE_ID. Similarly, messages generated whenever a service process exits are now made recognizable, - too. A taged message is also emitted whenever a unit enters the + too. A tagged message is also emitted whenever a unit enters the "dead" state on success. * systemd-run gained a new switch --working-directory= for configuring @@ -995,7 +1003,7 @@ CHANGES WITH 239: not created by systemd-sysusers anymore. NOTE: This has a chance of breaking nss-ldap and similar NSS modules - that embedd a network facing module into any process using getpwuid() + that embed a network facing module into any process using getpwuid() or related call: the dynamic allocation of the user ID for systemd-resolved.service means the service manager has to check NSS if the user name is already taken when forking off the service. Since @@ -1264,7 +1272,7 @@ CHANGES WITH 239: PrivateDevices=, ProtectSystem=, …) are used. This option is hence primarily useful for services that do not use any of the other file system namespacing options. One such service is systemd-udevd.service - wher this is now used by default. + where this is now used by default. * ConditionSecurity= gained a new value "uefi-secureboot" that is true when the system is booted in UEFI "secure mode". @@ -2247,7 +2255,7 @@ CHANGES WITH 234: /etc/machine-id. If the machine ID could not be determined, $KERNEL_INSTALL_MACHINE_ID will be empty. Plugins should not put anything in the entry directory (passed as the second argument) if - $KERNEL_INSTALL_MACHINE_ID is empty. For backwards compatiblity, a + $KERNEL_INSTALL_MACHINE_ID is empty. For backwards compatibility, a temporary directory is passed as the entry directory and removed after all the plugins exit. @@ -5975,7 +5983,7 @@ CHANGES WITH 214: * We temporarily dropped the "-l" switch for fsck invocations, since they collide with the flock() logic above. util-linux upstream has been changed already to avoid this conflict, - and we will readd "-l" as soon as util-linux with this + and we will re-add "-l" as soon as util-linux with this change has been released. * The dependency on libattr has been removed. Since a long @@ -6261,7 +6269,7 @@ CHANGES WITH 213: where the local administrator's configuration in /etc always overrides any other settings. - Contributions fron: Ali H. Caliskan, Alison Chaiken, Bas van + Contributions from: Ali H. Caliskan, Alison Chaiken, Bas van den Berg, Brandon Philips, Cristian RodrÃguez, Daniel Buch, Dan Kilman, Dave Reisner, David Härdeman, David Herrmann, David Strauss, Dimitris Spingos, Djalal Harouni, Eelco @@ -182,7 +182,7 @@ REQUIREMENTS: To build in directory build/: meson build/ && ninja -C build - Any configuration options can be specfied as -Darg=value... arguments + Any configuration options can be specified as -Darg=value... arguments to meson. After the build directory is initially configured, meson will refuse to run again, and options must be changed with: mesonconf -Darg=value... @@ -3,11 +3,11 @@ <a href="https://in.waw.pl/systemd-github-state/systemd-systemd-issues.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-issues-small.svg" alt="Count of open issues over time"></a> <a href="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests-small.svg" alt="Count of open pull requests over time"></a> [![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/> -[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/> [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/> [![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/> [![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/> -[![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/) +[![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/)<br/> +[![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master) ## Details @@ -19,6 +19,9 @@ Janitorial Clean-ups: Features: +* when killing due to service watchdog timeout maybe detect whether target + process is under ptracing and then log loudly and continue instead. + * tweak journald context caching. In addition to caching per-process attributes keyed by PID, cache per-cgroup attributes (i.e. the various xattrs we read) keyed by cgroup path, and guarded by ctime changes. This should provide us @@ -163,7 +166,7 @@ Features: selected user is resolvable in the service even if it ships its own /etc/passwd) * Fix DECIMAL_STR_MAX or DECIMAL_STR_WIDTH. One includes a trailing NUL, the - other doesn't. What a desaster. Probably to exclude it. Also + other doesn't. What a disaster. Probably to exclude it. Also DECIMAL_STR_WIDTH should probably add an extra "-" into account for negative numbers. @@ -192,7 +195,7 @@ Features: specific paths only like this. * add CopyFile= or so as unit file setting that may be used to copy files or - directory trees from the host to te services RootImage= and RootDirectory= + directory trees from the host to the services RootImage= and RootDirectory= environment. Which we can use for /etc/machine-id and in particular /etc/resolv.conf. Should be smart and do something useful on read-only images, for example fallback to read-only bind mounting the file instead. @@ -452,7 +455,7 @@ Features: * PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn) * there's probably something wrong with having user mounts below /sys, - as we have for debugfs. for exmaple, src/core/mount.c handles mounts + as we have for debugfs. for example, src/core/mount.c handles mounts prefixed with /sys generally special. http://lists.freedesktop.org/archives/systemd-devel/2015-June/032962.html @@ -514,7 +517,7 @@ Features: * systemctl: if some operation fails, show log output? -* systemctl edit: use equvalent of cat() to insert existing config as a comment, prepended with #. +* systemctl edit: use equivalent of cat() to insert existing config as a comment, prepended with #. Upon editor exit, lines with one # are removed, lines with two # are left with one #, etc. * exponential backoff in timesyncd when we cannot reach a server @@ -726,7 +729,7 @@ Features: - sd-journal: speed up sd_journal_get_data() with transparent hash table in bg - journald: when dropping msgs due to ratelimit make sure to write "dropped %u messages" not only when we are about to print the next - message that works, but alraedy after a short tiemout + message that works, but already after a short timeout - check if we can make journalctl by default use --follow mode inside of less if called without args? - maybe add API to send pairs of iovecs via sd_journal_send - journal: add a setgid "systemd-journal" utility to invoke from libsystemd-journal, which passes fds via STDOUT and does PK access diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..9a50e2eeb1 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,27 @@ +trigger: +- master + +jobs: +- job: FuzzBuzz + displayName: FuzzBuzz + + pool: + vmImage: 'ubuntu-latest' + + steps: + - script: | + set -e + ./travis-ci/managers/fuzzbuzz.sh + displayName: 'This is where it gets darker' + +- job: ASan_UBSan + displayName: ASan_UBSan + + pool: + vmImage: 'ubuntu-latest' + + steps: + - script: | + set -e + sudo ./travis-ci/managers/xenial.sh + displayName: 'This is where it gets darker' diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index d39a2251b0..4de8bce774 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -384,7 +384,7 @@ The following "tags" are possible: - "split-usr" — /usr is a separate file system and was not mounted when systemd was booted - "cgroups-missing" — the kernel was compiled without cgroup support or access - to expected interface files is resticted + to expected interface files is restricted - "var-run-bad" — /var/run is not a symlink to /run - "overflowuid-not-65534" — the kernel user ID used for "unknown" users (with NFS or user namespaces) is not 65534 diff --git a/catalog/systemd.it.catalog.in b/catalog/systemd.it.catalog.in index 8ce4fa5d92..c1375cca3e 100644 --- a/catalog/systemd.it.catalog.in +++ b/catalog/systemd.it.catalog.in @@ -1,9 +1,14 @@ # SPDX-License-Identifier: LGPL-2.1+ # -# Copyright © 2013 Daniele Medri +# Copyright © 2013-2019 Daniele Medri # Message catalog for systemd's own messages +# Italian translation +# Il formato dei messaggi di catalogo è descritto (in inglese) qui: +# https://www.freedesktop.org/wiki/Software/systemd/catalog + +# Subject: The Journal has been started -- f77379a8490b408bbe5f6940505a777b Subject: Il registro è stato avviato Defined-By: systemd @@ -12,6 +17,7 @@ Support: %SUPPORT_URL% Il processo relativo al registro di sistema è stato avviato, ha aperto i file in scrittura ed è ora pronto a gestire richieste. +# Subject: The Journal has been stopped -- d93fb3c9c24d451a97cea615ce59c00b Subject: Il registro è stato terminato Defined-By: systemd @@ -20,6 +26,7 @@ Support: %SUPPORT_URL% Il processo relativo al registro di sistema è stato terminato e ha chiuso tutti i file attivi. +# Subject: Disk space used by the journal -- ec387f577b844b8fa948f33cad9a75e6 Subject: Spazio disco utilizzato dal journal Defined-By: systemd @@ -27,14 +34,15 @@ Support: %SUPPORT_URL% @JOURNAL_NAME@ (@JOURNAL_PATH@) sta attualmente utilizzando @CURRENT_USE_PRETTY@. L'utilizzo massimo consentito è impostato a @MAX_USE_PRETTY@. -Lasciando liberi almeno @DISK_KEEP_FREE_PRETTY@ (dell'attuale @DISK_AVAILABLE_PRETTY@ di spazio libero). -Il limite di utilizzo forzato è quindi @LIMIT_PRETTY@, del quale @AVAILABLE_PRETTY@ sono ancora disponibili. +Si lasciano liberi almeno @DISK_KEEP_FREE_PRETTY@ (attuale spazio libero: @DISK_AVAILABLE_PRETTY@). +Il limite di utilizzo forzato è quindi @LIMIT_PRETTY@, con @AVAILABLE_PRETTY@ ancora disponibili. I limiti di controllo dello spazio disco utilizzati dal Journal possono essere configurati con le impostazioni SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= nel file di configurazione /etc/systemd/journald.conf. Guardare journald.conf(5) per i dettagli. +# Subject: Messages from a service have been suppressed -- a596d6fe7bfa4994828e72309e95d61e Subject: I messaggi di un servizio sono stati soppressi Defined-By: systemd @@ -47,18 +55,20 @@ I messaggi del servizio sono stati eliminati. 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 +I limiti oltre i quali si eliminano i messaggi si configurano con RateLimitIntervalSec= e RateLimitBurst= in /etc/systemd/journald.conf. Vedi journald.conf(5) per maggiori informazioni. +# Subject: Journal messages have been missed -- e9bf28e6e834481bb6f48f548ad13606 Subject: I messaggi di un servizio sono stati perduti Defined-By: systemd Support: %SUPPORT_URL% -I messaggi del kernel sono andati persi perché, il registro di sistema +I messaggi del kernel sono andati persi perché il registro di sistema non è stato in grado di gestirli abbastanza velocemente. +# Subject: Process @COREDUMP_PID@ (@COREDUMP_COMM@) dumped core -- fc2e22bc6ee647b6b90729ab34a250b1 Subject: Il processo @COREDUMP_PID@ (@COREDUMP_COMM@) ha generato un dump. Defined-By: systemd @@ -68,19 +78,21 @@ Documentation: man:core(5) Il processo @COREDUMP_PID@ (@COREDUMP_COMM@) si è bloccato generando un dump. Questo di solito capita per un errore di programmazione nell'applicazione e -dovrebbe essere segnalato al vendor come un bug. +dovrebbe essere segnalato come bug al vendor. +# Subject: Core file was truncated to @SIZE_LIMIT@ bytes -- 5aadd8e954dc4b1a8c954d63fd9e1137 Subject: Il Core file è stato troncato a @SIZE_LIMIT@ bytes. Defined-By: systemd Support: %SUPPORT_URL% Documentation: man:coredump.conf(5) -Il processo più memoria mappata del limite massimo configurato da systemd-coredump(8) +Il processo ha più memoria mappata del limite massimo configurato da systemd-coredump(8) per processare e memorizzare. Solo i primi @SIZE_LIMIT@ bytes sono stati salvati. Il file potrebbe essere ancora utile, ma strumenti come gdb(1) dovrebbero segnalare la troncatura. +# Subject: A new session @SESSION_ID@ has been created for user @USER_ID@ -- 8d45620c1a4348dbb17410da57c60c66 Subject: La nuova sessione @SESSION_ID@ è stata creata per l'utente @USER_ID@ Defined-By: systemd @@ -91,6 +103,7 @@ Una nuova sessione con ID @SESSION_ID@ è stata creata per l'utente @USER_ID@. Il processo primario della sessione è @LEADER@. +# Subject: A new seat @SEAT_ID@ has been terminated -- 3354939424b4456d9802ca8333ed424a Subject: La sessione @SESSION_ID@ è terminata Defined-By: systemd @@ -99,6 +112,7 @@ Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat La sessione con ID @SESSION_ID@ è terminata. +# Subject: A new seat @SEAT_ID@ is now available -- fcbefc5da23d428093f97c82a9290f7b Subject: La nuova postazione @SEAT_ID@ è ora disponibile Defined-By: systemd @@ -107,6 +121,7 @@ Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat La nuova postazione @SEAT_ID@ è stata configurata ed è ora disponibile. +# Subject: A seat @SEAT_ID@ has been removed -- e7852bfe46784ed0accde04bc864c2d5 Subject: La postazione @SEAT_ID@ è stata rimossa Defined-By: systemd @@ -115,6 +130,7 @@ Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat La postazione @SEAT_ID@ è stata rimossa e non è più disponibile. +# Subject: Time Change -- c7a787079b354eaaa9e77b371893cd27 Subject: Cambio d'orario Defined-By: systemd @@ -122,6 +138,7 @@ Support: %SUPPORT_URL% L'orologio di sistema è cambiato in @REALTIME@ microsecondi dal 1 gennaio, 1970. +# Subject: Time zone change to @TIMEZONE@ -- 45f82f4aef7a4bbf942ce861d1f20990 Subject: Il fuso orario è cambiato in @TIMEZONE@ Defined-By: systemd @@ -129,14 +146,15 @@ Support: %SUPPORT_URL% Il fuso orario di sistema è cambiato in @TIMEZONE@. +# Subject: System start-up is now complete -- b07a249cd024414a82dd00cd181378ff Subject: Avvio del sistema completato. Defined-By: systemd Support: %SUPPORT_URL% Tutti i servizi di sistema richiesti per la fase di avvio sono stati eseguiti -con successo. Nota che la macchina potrebbe non essere ancora pronta in quanto -i servizi attivati sono in fase di completamento. +con successo. La macchina potrebbe ancora attendere i servizi attivati sono +in fase di completamento. L'avvio del kernel ha richiesto @KERNEL_USEC@ microsecondi. @@ -144,17 +162,19 @@ L'avvio del disco RAM ha richiesto @INITRD_USEC@ microsecondi. L'avvio dello userspace ha richiesto @USERSPACE_USEC@ microsecondi. +# Subject: User manager start-up is now complete -- eed00a68ffd84e31882105fd973abdd1 -Subject: User manager start-up is now complete +Subject: L'istanza di gestione per l'utente è completata Defined-By: systemd Support: %SUPPORT_URL% L'istanza di gestione per l'utente @_UID@ è stata avviata. Tutti i servizi -interrogati sono stati avviati. Da notare che altri servizi potrebbero essere -ancora in fase di avvio o in attesa di essere avviati. +interrogati sono stati avviati. Altri servizi potrebbero essere ancora in +fase di avvio o in attesa di essere avviati. L'avvio dell'istanza ha impiegato @USERSPACE_USEC@ microsecondi. +# Subject: System sleep state @SLEEP@ entered -- 6bbd95ee977941e497c48be27c254128 Subject: Il sistema è entrato in fase di pausa @SLEEP@ Defined-By: systemd @@ -162,6 +182,7 @@ Support: %SUPPORT_URL% Il sistema è entrato nello stato di pausa @SLEEP@. +# Subject: System sleep state @SLEEP@ left -- 8811e6df2a8e40f58a94cea26f8ebf14 Subject: Il sistema è uscito dalla fase di pausa @SLEEP@ Defined-By: systemd @@ -169,6 +190,7 @@ Support: %SUPPORT_URL% Il sistema è uscito dallo stato di pausa @SLEEP@. +# Subject: System shutdown initiated -- 98268866d1d54a499c4e98921d93bc40 Subject: Il sistema è in fase di spegnimento Defined-By: systemd @@ -177,6 +199,7 @@ Support: %SUPPORT_URL% Systemd è in fase di spegnimento. Tutti i servizi di sistema saranno terminati e tutti i file systems smontati. +# Subject: Unit @UNIT@ has begun with start-up -- 7d4958e842da4a758f6c1cdc7b36dcc5 Subject: L'unità @UNIT@ inizia la fase di avvio Defined-By: systemd @@ -184,8 +207,9 @@ Support: %SUPPORT_URL% L'unità @UNIT@ ha iniziato la fase di avvio. +# Subject: Unit @UNIT@ has finished start-up -- 39f53479d3a045ac8e11786248231fbf -Subject: L'unità @UNIT@ termina la fase di avvio +Subject: L'unità @UNIT@ ha terminato la fase di avvio Defined-By: systemd Support: %SUPPORT_URL% @@ -193,20 +217,23 @@ L'unità @UNIT@ ha terminato la fase di avvio. La fase di avvio è @JOB_RESULT@. +# Subject: Unit @UNIT@ has begun shutting down -- de5b426a63be47a7b6ac3eaac82e2f6f -Subject: L'unità @UNIT@ inizia la fase di spegnimento +Subject: L'unità @UNIT@ ha iniziato la fase di spegnimento Defined-By: systemd Support: %SUPPORT_URL% L'unità @UNIT@ ha iniziato la fase di spegnimento. +# Subject Unit @UNIT@ has finished shutting down -- 9d1aaa27d60140bd96365438aad20286 -Subject: L'unità @UNIT@ termina la fase di spegnimento +Subject: L'unità @UNIT@ ha terminato la fase di spegnimento Defined-By: systemd Support: %SUPPORT_URL% L'unità @UNIT@ ha terminato la fase di spegnimento. +# Subject: Unit @UNIT@ has failed -- be02cf6855d2428ba40df7e9d022f03d Subject: L'unità @UNIT@ è fallita Defined-By: systemd @@ -216,22 +243,25 @@ L'unità @UNIT@ è fallita. Il risultato è @JOB_RESULT@. +# Subject: Unit @UNIT@ has begun with reloading its configuration -- d34d037fff1847e6ae669a370e694725 -Subject: L'unità @UNIT@ inizia a caricare la propria configurazione +Subject: L'unità @UNIT@ è iniziata con il ricaricamento della propria configurazione Defined-By: systemd Support: %SUPPORT_URL% -L'unità @UNIT@ è iniziata ricaricando la propria configurazione +L'unità @UNIT@ è iniziata con il ricaricamento della propria configurazione +# Subject: Unit @UNIT@ has finished reloading its configuration -- 7b05ebc668384222baa8881179cfda54 -Subject: L'unità @UNIT@ termina il caricamento della propria configurazione +Subject: L'unità @UNIT@ è terminata con il ricaricamento della propria configurazione Defined-By: systemd Support: %SUPPORT_URL% -L'unità @UNIT@ è terminata ricaricando la propria configurazione +L'unità @UNIT@ è terminata con il ricaricamento della propria configurazione Il risultato è @JOB_RESULT@. +# Subject: Process @EXECUTABLE@ could not be executed -- 641257651c1b4ec9a8624d7a40a9e1e7 Subject: Il processo @EXECUTABLE@ non può essere eseguito Defined-By: systemd @@ -241,6 +271,7 @@ Il processo @EXECUTABLE@ non può essere eseguito e termina. Il numero di errore restituito durante l'esecuzione del processo è @ERRNO@. +# Subject: One or more messages could not be forwarded to syslog -- 0027229ca0644181a76c4e92458afa2e Subject: Uno o più messaggi non possono essere inoltrati a syslog Defined-By: systemd @@ -251,6 +282,7 @@ eseguito in parallelo a journald. Questo di solito capita perché, l'implementazione di syslog non sta al passo con la velocità dei messaggi accodati. +# Subject: Mount point is not empty -- 1dee0369c7fc4736b7099b38ecb46ee7 Subject: Il punto di montaggio non è vuoto Defined-By: systemd @@ -262,6 +294,7 @@ Questo non interferisce con il montaggio, ma i file pre-esistenti in questa directory diventano inaccessibili. Per visualizzare i file, si suggerisce di montare manualmente il file system indicato in una posizione secondaria. +# Subject: A virtual machine or container has been started -- 24d8d4452573402496068381a6312df2 Subject: Avviata macchina virtuale o container Defined-By: systemd @@ -270,13 +303,15 @@ Support: %SUPPORT_URL% La macchina virtuale @NAME@ con PID primario @LEADER@ è stata avviata ed è pronta all'uso. +# Subject: A virtual machine or container has been terminated -- 58432bd3bace477cb514b56381b8a758 Subject: Terminata macchina virtuale o container Defined-By: systemd Support: %SUPPORT_URL% -La macchina virtuale @NAME@ con PID primario @LEADER@ è stata spenta. +La macchina virtuale @NAME@ con PID primario @LEADER@ è stata terminata. +# Subject: DNSSEC mode has been turned off, as server doesn't support it -- 36db2dfa5a9045e1bd4af5f93e1cf057 Subject: La modalità DNSSEC è stata spenta, il server non la supporta Defined-By: systemd @@ -284,18 +319,19 @@ Support: %SUPPORT_URL% Documentation: man:systemd-resolved.service(8) resolved.conf(5) Il servizio di risoluzione (systemd-resolved.service) ha rilevato che il -server DNS indicato non supporta DNSSEC e la validazione DNSSEC è stata -conseguentemente disabilitata. +server DNS indicato non supporta DNSSEC e la validazione di quest'ultimo tipo +è stata conseguentemente disabilitata. Ciò avverrà se DNSSEC=allow-downgrade è configurato nel file -resolved.conf e il server DNS indicato è incompatibile con DNSSEC. Da notare -che in questo modo ci si espone ad attacchi DNSSEC downgrade, e un aggressore +resolved.conf e il server DNS indicato è incompatibile con DNSSEC. +In questo modo ci si espone ad attacchi "DNSSEC downgrade", e un aggressore potrebbe disabilitare la validazione DNSSEC sul sistema inserendo risposte DNS nel canale di comunicazione. -Questo evento potrebbe essere indice che il DNS server è forse incompatibile -con DNSSEC o che un aggressore è riuscito nel suo intento malevolo. +Questo evento potrebbe indicare un'incompatibilità del DNS Server con DNSSEC o +che un aggressore è riuscito nel suo intento malevolo. +# Subject: DNSSEC validation failed -- 1675d7f172174098b1108bf8c7dc8f5d Subject: La validazione DNSSEC è fallita Defined-By: systemd @@ -306,6 +342,7 @@ Una query DNS o un dato hanno fatto fallire la validazione DNSSEC. Questo è usualmente un segnale che il canale di comunicazione utilizzato è stato manomesso. +# Subject: A DNSSEC trust anchor has been revoked -- 4d4408cfd0d144859184d1e65d7c8a65 Subject: Un trust anchor DNSSEC è stato revocato Defined-By: systemd @@ -315,3 +352,76 @@ Documentation: man:systemd-resolved.service(8) Un trust anchor DNSSEC è stato revocato. Un nuovo punto di fiducia è stato riconfigurato o il sistema operativo deve essere aggiornato per fornire un nuovo ancoraggio. + +# Subject: Automatic restarting of a unit has been scheduled +-- 5eb03494b6584870a536b337290809b3 +Subject: Il riavvio automatico di un'unità è stato schedulato +Defined-By: systemd +Support: %SUPPORT_URL% + +Il riavvio automatico dell'unità @UNIT@ è stato schedulato, come risultato +delle impostazioni configurate in Restart=. + +# Subject: Resources consumed by unit runtime +-- ae8f7b866b0347b9af31fe1c80b127c0 +Subject: Risorse utilizzate dall'unità in esecuzione +Defined-By: systemd +Support: %SUPPORT_URL% + +Unità @UNIT@ terminata consumando le indicate risorse. + +# Subject: Unit succeeded +-- 7ad2d189f7e94e70a38c781354912448 +Subject: Unità terminata +Defined-By: systemd +Support: %SUPPORT_URL% + +Unità @UNIT@ entrata con successo nello stato 'dead' (morto). + +# Subject: Unit failed +-- d9b373ed55a64feb8242e02dbe79a49c +Subject: Unit fallita +Defined-By: systemd +Support: %SUPPORT_URL% + +Unità @UNIT@ entrata nello stato 'failed' (fallito) con risultato '@UNIT_RESULT@'. + +# Subject: Unit process exited +-- 98e322203f7a4ed290d09fe03c09fe15 +Subject: Uscito processo unità +Defined-By: systemd +Support: %SUPPORT_URL% + +Un processo @COMMAND@ appartenente all'unità @UNIT@ è uscito. + +Il codice di uscita del processo è '@EXIT_CODE@' ed è uscito con @EXIT_STATUS@. + +# Subject: The system is configured in a way that might cause problems +-- 50876a9db00f4c40bde1a2ad381c3a1b +Subject: Il sistema è configurato in un modo che potrebbe causare problemi +Defined-By: systemd +Support: %SUPPORT_URL% + +I seguenti "tags" sono possibili: +- "split-usr" — /usr è un file system separato e non è stato montato all'avvio di systemd +- "cgroups-missing" — il kernel era compilato senza supporto cgroup o l'accesso ai + file attesi è ristretto. +- "var-run-bad" — /var/run non è un link simbolico (symlink) a /run +- "overflowuid-not-65534" — l'ID usato dal kernel per gli utenti "unknown" (tipo + NFS o user namespace) non è 65534 +- "overflowgid-not-65534" — l'ID usato dal kernel per il gruppo "unknown" (tipo + NFS o user namespace) non è 65534 +L'attuale sistema è taggato come @TAINT@. + +# Subject: A process of @UNIT@ unit has been killed by the OOM killer. +-- fe6faa94e7774663a0da52717891d8ef +Subject: Un processo dell'unità @UNIT@ è stato terminato da un OOM killer. +Defined-By: systemd +Support: %SUPPORT_URL% + +Un processo dell'unità @UNIT@ è stato terminato dalla logica del kernel Linux per +gestire gli eventi out-of-memory (OOM). In altri termini, il sistema ha +poca memoria e dovrebbe essere liberata. Un processo associato con @UNIT@ è +stato identificato come il processo ideale da terminare e così è stato. + +La pressione sulla memoira potrebbe o meno essere causata da @UNIT@. diff --git a/coccinelle/const-strlen.cocci b/coccinelle/const-strlen.disabled index 38bf9b118f..30a6e5a88e 100644 --- a/coccinelle/const-strlen.cocci +++ b/coccinelle/const-strlen.disabled @@ -1,8 +1,12 @@ @@ constant s; @@ +( +#define STRLEN +& - sizeof(s)-1 + STRLEN(s) +) @@ constant s; @@ diff --git a/coccinelle/debug-logging.cocci b/coccinelle/debug-logging.cocci index 9084cf773b..a679dab011 100644 --- a/coccinelle/debug-logging.cocci +++ b/coccinelle/debug-logging.cocci @@ -1,8 +1,16 @@ @@ @@ +( +#define DEBUG_LOGGING +& - _unlikely_(log_get_max_level() >= LOG_DEBUG) + DEBUG_LOGGING +) @@ @@ +( +#define DEBUG_LOGGING +& - log_get_max_level() >= LOG_DEBUG + DEBUG_LOGGING +) diff --git a/coccinelle/dup-fcntl.cocci b/coccinelle/dup-fcntl.cocci index ef13564282..8b133b3a24 100644 --- a/coccinelle/dup-fcntl.cocci +++ b/coccinelle/dup-fcntl.cocci @@ -1,5 +1,7 @@ @@ +/* We want to stick with dup() in test-fd-util.c */ +position p : script:python() { p[0].file != "src/test/test-fd-util.c" }; expression fd; @@ -- dup(fd) +- dup@p(fd) + fcntl(fd, F_DUPFD, 3) diff --git a/coccinelle/empty-to-null.cocci b/coccinelle/empty-to-null.cocci index fbc75b9c34..bc6c656e79 100644 --- a/coccinelle/empty-to-null.cocci +++ b/coccinelle/empty-to-null.cocci @@ -1,5 +1,8 @@ @@ +/* Avoid running this transformation on the empty_to_null function itself */ +position p : script:python() { p[0].current_element != "empty_to_null" }; expression s; @@ -- isempty(s) ? NULL : s + +- isempty@p(s) ? NULL : s + empty_to_null(s) diff --git a/coccinelle/flags-set.cocci b/coccinelle/flags-set.cocci index 1a70717e76..73966b02e5 100644 --- a/coccinelle/flags-set.cocci +++ b/coccinelle/flags-set.cocci @@ -1,15 +1,16 @@ @@ +/* Disable this transformation for the securebits-util.h, as it makes + * the expression there confusing. */ +position p : script:python() { p[0].file != "src/shared/securebits-util.h" }; expression x, y; @@ -- ((x) & (y)) == (y) +( +- ((x@p) & (y)) == (y) + FLAGS_SET(x, y) -@@ -expression x, y; -@@ -- (x & (y)) == (y) +| +- (x@p & (y)) == (y) + FLAGS_SET(x, y) -@@ -expression x, y; -@@ -- ((x) & y) == y +| +- ((x@p) & y) == y + FLAGS_SET(x, y) +) diff --git a/coccinelle/in_set.cocci b/coccinelle/in_set.cocci index 12d5475fd9..2c9b94ceb6 100644 --- a/coccinelle/in_set.cocci +++ b/coccinelle/in_set.cocci @@ -1,54 +1,37 @@ @@ expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7, n8, n9; -@@ +/* Exclude JsonVariant * from the transformation, as it can't work with the + * current version of the IN_SET macro */ +typedef JsonVariant; +type T != JsonVariant*; +constant T n0, n1, n2, n3, n4, n5, n6, n7, n8, n9; +@@ + +( - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7 || e == n8 || e == n9 + IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7, n8; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7 || e == n8 + IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7 + IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 + IN_SET(e, n0, n1, n2, n3, n4, n5, n6) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 + IN_SET(e, n0, n1, n2, n3, n4, n5) -@@ -expression e; -constant n0, n1, n2, n3, n4; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 + IN_SET(e, n0, n1, n2, n3, n4) -@@ -expression e; -constant n0, n1, n2, n3; -@@ +| - e == n0 || e == n1 || e == n2 || e == n3 + IN_SET(e, n0, n1, n2, n3) -@@ -expression e; -constant n0, n1, n2; -@@ +| - e == n0 || e == n1 || e == n2 + IN_SET(e, n0, n1, n2) -@@ -expression e; -constant n0, n1; -@@ +| - e == n0 || e == n1 + IN_SET(e, n0, n1) +) diff --git a/coccinelle/isempty.cocci b/coccinelle/isempty.cocci index d8d5275889..e0a9f07ca6 100644 --- a/coccinelle/isempty.cocci +++ b/coccinelle/isempty.cocci @@ -1,60 +1,42 @@ @@ +/* Disable this transformation for the test-string-util.c */ +position p : script:python() { p[0].file != "src/test/test-string-util.c" }; expression s; @@ -- strv_length(s) == 0 +( +- strv_length@p(s) == 0 + strv_isempty(s) -@@ -expression s; -@@ -- strv_length(s) <= 0 +| +- strv_length@p(s) <= 0 + strv_isempty(s) -@@ -expression s; -@@ -- strv_length(s) > 0 +| +- strv_length@p(s) > 0 + !strv_isempty(s) -@@ -expression s; -@@ -- strv_length(s) != 0 +| +- strv_length@p(s) != 0 + !strv_isempty(s) -@@ -expression s; -@@ -- strlen(s) == 0 +| +- strlen@p(s) == 0 + isempty(s) -@@ -expression s; -@@ -- strlen(s) <= 0 +| +- strlen@p(s) <= 0 + isempty(s) -@@ -expression s; -@@ -- strlen(s) > 0 +| +- strlen@p(s) > 0 + !isempty(s) -@@ -expression s; -@@ -- strlen(s) != 0 +| +- strlen@p(s) != 0 + !isempty(s) -@@ -expression s; -@@ -- strlen_ptr(s) == 0 +| +- strlen_ptr@p(s) == 0 + isempty(s) -@@ -expression s; -@@ -- strlen_ptr(s) <= 0 +| +- strlen_ptr@p(s) <= 0 + isempty(s) -@@ -expression s; -@@ -- strlen_ptr(s) > 0 +| +- strlen_ptr@p(s) > 0 + !isempty(s) -@@ -expression s; -@@ -- strlen_ptr(s) != 0 +| +- strlen_ptr@p(s) != 0 + !isempty(s) +) diff --git a/coccinelle/memzero.cocci b/coccinelle/memzero.cocci index ebdc3f6a2a..8198cc84b4 100644 --- a/coccinelle/memzero.cocci +++ b/coccinelle/memzero.cocci @@ -21,10 +21,18 @@ expression s; @@ expression a, b; @@ +( +#define memzero +& - memset(a, 0, b) + memzero(a, b) +) @@ expression a, b; @@ +( +#define memzero +& - bzero(a, b) + memzero(a, b) +) diff --git a/coccinelle/mfree_return.cocci b/coccinelle/mfree_return.cocci index 8119fe07f2..15e6c7d566 100644 --- a/coccinelle/mfree_return.cocci +++ b/coccinelle/mfree_return.cocci @@ -1,6 +1,8 @@ @@ -expression p; +/* Avoid running this transformation on the mfree function itself */ +position p : script:python() { p[0].current_element != "mfree" }; +expression e; @@ -- free(p); +- free@p(e); - return NULL; -+ return mfree(p); ++ return mfree(e); diff --git a/coccinelle/not_in_set.cocci b/coccinelle/not_in_set.cocci index 7cf98500cd..aed2c3490c 100644 --- a/coccinelle/not_in_set.cocci +++ b/coccinelle/not_in_set.cocci @@ -1,54 +1,34 @@ @@ expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7, n8, n9; +typedef JsonVariant; +type T != JsonVariant*; +constant T n0, n1, n2, n3, n4, n5, n6, n7, n8, n9; @@ +( - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7 && e != n8 && e != n9 + !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7, n8; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7 && e != n8 + !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6, n7; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7 + !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5, n6; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 + !IN_SET(e, n0, n1, n2, n3, n4, n5, n6) -@@ -expression e; -constant n0, n1, n2, n3, n4, n5; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 + !IN_SET(e, n0, n1, n2, n3, n4, n5) -@@ -expression e; -constant n0, n1, n2, n3, n4; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 + !IN_SET(e, n0, n1, n2, n3, n4) -@@ -expression e; -constant n0, n1, n2, n3; -@@ +| - e != n0 && e != n1 && e != n2 && e != n3 + !IN_SET(e, n0, n1, n2, n3) -@@ -expression e; -constant n0, n1, n2; -@@ +| - e != n0 && e != n1 && e != n2 + !IN_SET(e, n0, n1, n2) -@@ -expression e; -constant n0, n1; -@@ +| - e != n0 && e != n1 + !IN_SET(e, n0, n1) +) diff --git a/coccinelle/run-coccinelle.sh b/coccinelle/run-coccinelle.sh index 520de0ac42..be80a76a5f 100755 --- a/coccinelle/run-coccinelle.sh +++ b/coccinelle/run-coccinelle.sh @@ -1,9 +1,25 @@ #!/bin/bash -e +# Exclude following paths from the Coccinelle transformations +EXCLUDED_PATHS=( + "src/boot/efi/*" + "src/shared/linux/*" + "src/basic/linux/*" + # Symlinked to test-bus-vtable-cc.cc, which causes issues with the IN_SET macro + "src/libsystemd/sd-bus/test-bus-vtable.c" +) + top="$(git rev-parse --show-toplevel)" -files="$(git ls-files ':/*.[ch]')" +iso_defs="$top/coccinelle/systemd-definitions.iso" args= +# Create an array from files tracked by git... +mapfile -t files < <(git ls-files ':/*.[ch]') +# ...and filter everything that matches patterns from EXCLUDED_PATHS +for excl in "${EXCLUDED_PATHS[@]}"; do + files=(${files[@]//$excl}) +done + case "$1" in -i) args="$args --in-place" @@ -21,7 +37,7 @@ for SCRIPT in ${@-$top/coccinelle/*.cocci} ; do TMPFILE=`mktemp` echo "+ spatch --sp-file $SCRIPT $args ..." parallel --halt now,fail=1 --keep-order --noswap --max-args=20 \ - spatch --sp-file $SCRIPT $args ::: $files \ + spatch --iso-file $iso_defs --sp-file $SCRIPT $args ::: "${files[@]}" \ 2>"$TMPFILE" || cat "$TMPFILE" echo -e "--x-- Processed $SCRIPT --x--\n" done diff --git a/coccinelle/sd_event_source_disable_unref.cocci b/coccinelle/sd_event_source_disable_unref.cocci new file mode 100644 index 0000000000..2763fefac9 --- /dev/null +++ b/coccinelle/sd_event_source_disable_unref.cocci @@ -0,0 +1,36 @@ +@@ +expression p; +@@ +- if (p) { +- (void) sd_event_source_set_enabled(p, SD_EVENT_OFF); +- p = sd_event_source_unref(p); +- } ++ p = sd_event_source_disable_unref(p); +@@ +expression p; +@@ +- if (p) { +- sd_event_source_set_enabled(p, SD_EVENT_OFF); +- sd_event_source_unref(p); +- } ++ sd_event_source_disable_unref(p); +@@ +expression p; +@@ +- if (p) { +- (void) sd_event_source_set_enabled(p, SD_EVENT_OFF); +- sd_event_source_unref(p); +- } ++ sd_event_source_disable_unref(p); +@@ +expression p; +@@ +- (void) sd_event_source_set_enabled(p, SD_EVENT_OFF); +- sd_event_source_unref(p); ++ sd_event_source_disable_unref(p); +@@ +expression p; +@@ +- sd_event_source_set_enabled(p, SD_EVENT_OFF); +- sd_event_source_unref(p); ++ sd_event_source_disable_unref(p); diff --git a/coccinelle/strempty.cocci b/coccinelle/strempty.cocci index 13ceb338f1..7901da3652 100644 --- a/coccinelle/strempty.cocci +++ b/coccinelle/strempty.cocci @@ -1,48 +1,60 @@ @@ +/* Avoid running this transformation on the strempty function itself */ +position p : script:python() { p[0].current_element != "strempty" }; expression s; @@ -- s ?: "" +( +- s@p ?: "" + strempty(s) -@@ -expression s; -@@ -- s ? s : "" +| +- s@p ? s : "" + strempty(s) +) + @@ +position p : script:python() { p[0].current_element != "strempty" }; expression s; @@ -- if (!s) +- if (!s@p) - s = ""; + s = strempty(s); + @@ +position p : script:python() { p[0].current_element != "strnull" }; expression s; @@ -- s ?: "(null)" +( +- s@p ?: "(null)" + strnull(s) -@@ -expression s; -@@ -- s ? s : "(null)" +| +- s@p ? s : "(null)" + strnull(s) +) + @@ +position p : script:python() { p[0].current_element != "strnull" }; expression s; @@ -- if (!s) +- if (!s@p) - s = "(null)"; + s = strnull(s); + @@ +position p : script:python() { p[0].current_element != "strna" }; expression s; @@ -- s ?: "n/a" +( +- s@p ?: "n/a" + strna(s) -@@ -expression s; -@@ -- s ? s : "n/a" +| +- s@p ? s : "n/a" + strna(s) +) + @@ +position p : script:python() { p[0].current_element != "strna" }; expression s; @@ -- if (!s) +- if (!s@p) - s = "n/a"; + s = strna(s); diff --git a/coccinelle/synthetic-errno.cocci b/coccinelle/synthetic-errno.cocci index 645bfc945f..3ddb69cb4c 100644 --- a/coccinelle/synthetic-errno.cocci +++ b/coccinelle/synthetic-errno.cocci @@ -2,9 +2,15 @@ expression e; expression list args; @@ +( +/* Ignore one specific case in src/shared/bootspec.c where we want to stick + * with the log_debug() + return pattern */ +log_debug("Found no default boot entry :("); +| - log_debug(args); - return -e; + return log_debug_errno(SYNTHETIC_ERRNO(e), args); +) @@ expression e; expression list args; diff --git a/coccinelle/systemd-definitions.iso b/coccinelle/systemd-definitions.iso new file mode 100644 index 0000000000..92db763a29 --- /dev/null +++ b/coccinelle/systemd-definitions.iso @@ -0,0 +1,20 @@ +/* Statement isomorphisms - replace explicit checks against NULL with a + * shorter variant, which relies on C's downgrade-to-bool feature. + * The expression metavariables should be declared as pointers, however, + * that doesn't work well with complex expressions like: + * if (UNIT(p)->default_dependencies != NULL) + */ + +Statement +@@ +expression X; +statement S; +@@ +if (X == NULL) S => if (!X) S + +Statement +@@ +expression X; +statement S; +@@ +if (X != NULL) S => if (X) S diff --git a/coccinelle/take-fd.cocci b/coccinelle/take-fd.cocci index ba242483cd..f7124e7896 100644 --- a/coccinelle/take-fd.cocci +++ b/coccinelle/take-fd.cocci @@ -6,8 +6,15 @@ expression q; - q = -1; - return p; + return TAKE_FD(q); + +/* The ideal solution would use 'local idexpression' to avoid matching errno, + * which is a global variable. However, 'idexpression' nor 'identifier' + * would match, for example, "x->fd", which is considered 'expression' in + * the SmPL grammar + */ @@ -expression p, q; +expression p != errno; +expression q; @@ - p = q; - q = -1; diff --git a/docs/TEMPORARY_DIRECTORIES.md b/docs/TEMPORARY_DIRECTORIES.md index 0ea88f8fc6..9271e0e478 100644 --- a/docs/TEMPORARY_DIRECTORIES.md +++ b/docs/TEMPORARY_DIRECTORIES.md @@ -154,7 +154,7 @@ strategies to avoid these issues: in these directories when they are no longer needed, in particular when the program dies unexpectedly. Note: this strategy is only really suitable for packages that operate in a "system wide singleton" fashion with "long" - persistance of its data or state, i.e. as opposed to programs that run in + persistence of its data or state, i.e. as opposed to programs that run in multiple parallel or short-living instances. This is because a private directory under `/run` (and the other mentioned directories) is itself system and package specific singleton with greater longevity. diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index 3aa68c0a26..469ef7ce31 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -227,6 +227,7 @@ All cgroup/resource control settings are available for transient units ✓ CPUQuota= ✓ CPUQuotaPeriodSec= ✓ MemoryAccounting= +✓ DefaultMemoryMin= ✓ MemoryMin= ✓ DefaultMemoryLow= ✓ MemoryLow= diff --git a/fuzz.yaml b/fuzz.yaml new file mode 100644 index 0000000000..f64af60e01 --- /dev/null +++ b/fuzz.yaml @@ -0,0 +1,32 @@ +base: ubuntu:16.04 +language: c +setup: +- sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list" +- sudo apt-get update -y +- sudo apt-get build-dep -y systemd +- sudo apt-get install -y python3-pip +- pip3 install meson ninja +- export PATH="$HOME/.local/bin/:$PATH" +- CC=$FUZZ_CC CXX=$FUZZ_CXX meson -Dfuzzbuzz=true -Dfuzzbuzz-engine-dir=$(dirname "$FUZZ_ENGINE") -Dfuzzbuzz-engine=$(cut -d. -f1 <(basename "$FUZZ_ENGINE")) -Db_lundef=false ./build +- ninja -v -C ./build fuzzers +environment: +targets: +- name: fuzz-compress + harness: + binary: ./build/fuzz-compress +- name: fuzz-unit-file + harness: + binary: ./build/fuzz-unit-file + corpus: ./test/fuzz/fuzz-unit-file +- name: fuzz-journald-syslog + harness: + binary: ./build/fuzz-journald-syslog + corpus: ./test/fuzz/fuzz-journald-syslog +- name: fuzz-netdev-parser + harness: + binary: ./build/fuzz-netdev-parser + corpus: ./test/fuzz/fuzz-netdev-parser +- name: fuzz-network-parser + harness: + binary: ./build/fuzz-network-parser + corpus: ./test/fuzz/fuzz-network-parser diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index 4f94342a09..d3664f4f09 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -232,6 +232,13 @@ evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLati EVDEV_ABS_35=79:1841:22 EVDEV_ABS_36=140:1325:29 +# Dell Latitude E7250 +evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE7250* + EVDEV_ABS_00=179:3903:38 + EVDEV_ABS_01=277:1916:32 + EVDEV_ABS_35=179:3903:38 + EVDEV_ABS_36=277:1916:32 + # Dell Latitude E7470 evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE7470* EVDEV_ABS_00=29:2930:30 diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 4762c68a1b..62957d2725 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -98,8 +98,7 @@ # common keys evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn* -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pn* -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPackard*Bell*:pn* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pnA0A1*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svneMachines:pneMachines*E725:pvr* KEYBOARD_KEY_86=wlan # Fn+F3 or Fn+Q for comunication key KEYBOARD_KEY_a5=help # Fn+F1 @@ -183,6 +182,11 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*1640:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAOA*:pvr* KEYBOARD_KEY_a9=!switchvideomode # Fn+F5 +# Packard Bell and Gateway models +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pn* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPackard*Bell*:pn* + KEYBOARD_KEY_86=wlan # Fn+F3 or Fn+Q for comunication key + ########################################################### # Alienware ########################################################### @@ -837,6 +841,168 @@ evdev:input:b0003v04B3p301B* # Logitech ########################################################### +# 27MHz wireless keyboards, these all have a PID of 00?? and all send c10xx +# logitech custom consumer usage-page codes. The mappings below are the most +# common, but some mapping may differ, especially the Fn F1-F12 mappings +evdev:input:b0003v046Dp00* + KEYBOARD_KEY_c0183=media # HUT:config, kbd:Media/Music player button + KEYBOARD_KEY_c1001=chat # Messenger button + KEYBOARD_KEY_c1002=camera # Webcam button + KEYBOARD_KEY_c1003=audio # Music Browser button + KEYBOARD_KEY_c1004=video # Video Browser button + KEYBOARD_KEY_c1005=images # Image Browser button + KEYBOARD_KEY_c100a=documents # Document Browser button + KEYBOARD_KEY_c100b=rewind # Rewind button + KEYBOARD_KEY_c100c=fastforward # Fast Forward button + KEYBOARD_KEY_c100f=f14 # Track 1 button → f14 → XF86Launch5 + KEYBOARD_KEY_c1010=f15 # Track 2 button → f15 → XF86Launch6 + KEYBOARD_KEY_c1011=channeldown # Playlist back button + KEYBOARD_KEY_c1012=channelup # Playlist advance button + KEYBOARD_KEY_c1013=camera # Webcam button + KEYBOARD_KEY_c1014=coffee # Status button + KEYBOARD_KEY_c1015=record # Record symbol button + KEYBOARD_KEY_c1016=sound # Flame/CD burning → sound → XF86AudioPreset + KEYBOARD_KEY_c1017=ejectcd # Eject button + KEYBOARD_KEY_c1018=config # Remote-control ico + KEYBOARD_KEY_c1019=f14 # Preset 1 → f14 → XF86Launch5 + KEYBOARD_KEY_c101a=f15 # Preset 2 → f15 → XF86Launch6 + KEYBOARD_KEY_c101b=f16 # Preset 3 → f16 → XF86Launch7 + KEYBOARD_KEY_c101c=cyclewindows # 2 overlapping windows icon + KEYBOARD_KEY_c101f=zoomout # zoom - button / - side of zoomrocker + KEYBOARD_KEY_c1020=zoomin # zoom + button / + side off zoom rocker + KEYBOARD_KEY_c1021=zoomreset # 100% symbol on kbd left side + KEYBOARD_KEY_c1023=close # [x] symbol on kbd left side + KEYBOARD_KEY_c1027=menu # Hamburger menu icon + KEYBOARD_KEY_c1028=angle # Rotate button + KEYBOARD_KEY_c1029=shuffle # Shuffle button + KEYBOARD_KEY_c102a=back # Back button + KEYBOARD_KEY_c102b=cyclewindows # Empty window icon + KEYBOARD_KEY_c102d=www # www text + magnifierglass icon + KEYBOARD_KEY_c1031=connect # Pickup phone button → connect → XF86Go + KEYBOARD_KEY_c1032=cancel # Hangup phone button → cancel → Cancel + KEYBOARD_KEY_c1041=help # Help text or icon (Fn + F1) + KEYBOARD_KEY_c1042=wordprocessor # Word icon (Fn + F2) + KEYBOARD_KEY_c1043=spreadsheet # Excel icon (Fn + F3) + KEYBOARD_KEY_c1044=presentation # Presentation icon (Fn + F4) + KEYBOARD_KEY_c1045=undo # Undo Icon (Fn + F5) + KEYBOARD_KEY_c1046=redo # Redo Icon (Fn + F6) + KEYBOARD_KEY_c1047=print # Printer Icon (Fn + F7) + KEYBOARD_KEY_c1048=save # Floppy Icon (Fn + F8) + KEYBOARD_KEY_c1049=prog1 # Smartkey A (Fn + F9) → XF86Launch1 + KEYBOARD_KEY_c104a=prog2 # Smartkey B (Fn + F10) → XF86Launch2 + KEYBOARD_KEY_c104b=prog3 # Smartkey C (Fn + F11) → XF86Launch3 + KEYBOARD_KEY_c104c=prog4 # Smartkey D (Fn + F12) → XF86Launch4 + +# Cordless Access Keyboard (27 MHz, modelnumber Y-RH35) +evdev:input:b0003v046Dp0042* + KEYBOARD_KEY_c1041=new + KEYBOARD_KEY_c1042=reply + KEYBOARD_KEY_c1043=forward + KEYBOARD_KEY_c1044=send + KEYBOARD_KEY_c1045=previoussong + KEYBOARD_KEY_c1046=nextsong + KEYBOARD_KEY_c1047=playpause + KEYBOARD_KEY_c1048=stopcd + KEYBOARD_KEY_c1049=file + KEYBOARD_KEY_c104a=documents + KEYBOARD_KEY_c104b=images + KEYBOARD_KEY_c104c=audio + +# "Cordless Rechargeable Desktop" keyboard (27 MHz, modelnumber Y-RK49) +evdev:input:b0003v046Dp0045* + KEYBOARD_KEY_c1041=new + KEYBOARD_KEY_c1042=reply + KEYBOARD_KEY_c1043=forward + KEYBOARD_KEY_c1044=send + KEYBOARD_KEY_c1049=file + KEYBOARD_KEY_c104a=documents + KEYBOARD_KEY_c104b=images + KEYBOARD_KEY_c104c=audio + +# S510 keyboard (27 MHz, modelnumber Y-RAK73) +evdev:input:b0003v046Dp0056* + KEYBOARD_KEY_c1041=battery # Battery icon (Fn + F1) + +# MX3000 keyboard (27 MHz, modelnumber Y-RAM74) +# We ignore the scroll up / down keypress events since these buttons also +# generate scroll-wheel events and we do not want to generate duplicate events +# Note if the "Special Button Function" in the HID++ features register gets +# cleared then the scroll-wheel events for these buttons go away and then +# tilting the scrollwheel left/right starts sending c1022 / c1024 events +evdev:input:b0003v046Dp0057* + KEYBOARD_KEY_c1041=battery # Battery icon (Fn + F1) + +#KEYBOARD_KEY_c101d=scrolldown # Button below scrollwheel (see note above) +#KEYBOARD_KEY_c101e=scrollup # Button above scrollwheel (see note above) +#KEYBOARD_KEY_c1022=scrollleft # Left click on scroll-wheel (see note above) +#KEYBOARD_KEY_c1024=scrollright # Right click on scroll-wheel (see note above) + +# MX3200 keyboard (27 MHz, modelnumber Y-RAV80) +evdev:input:b0003v046Dp005C* + KEYBOARD_KEY_c1001=phone # VOIP button + KEYBOARD_KEY_c1016=record # Record button + KEYBOARD_KEY_c1041=wordprocessor # Word icon (Fn + F1) + KEYBOARD_KEY_c1042=spreadsheet # Excel icon (Fn + F2) + KEYBOARD_KEY_c1043=calendar # Calendar icon (Fn + F3) + KEYBOARD_KEY_c1044=documents # My Documents icon (Fn + F4) + KEYBOARD_KEY_c1045=prog1 # Smartkey A (Fn + F5) → XF86Launch1 + KEYBOARD_KEY_c1046=prog2 # Smartkey B (Fn + F6) → XF86Launch2 + KEYBOARD_KEY_c1047=prog3 # Smartkey C (Fn + F7) → XF86Launch3 + KEYBOARD_KEY_c1048=prog4 # Smartkey D (Fn + F8) → XF86Launch4 + +# EX100 keyboard (27 MHz, modelnumber Y-RBH94) +evdev:input:b0003v046Dp0065* + KEYBOARD_KEY_c104b=battery # Battery icon (Fn + F11) + KEYBOARD_KEY_c104c=ejectcd # Eject icon (Fn + F12) + +# S520 keyboard (27 MHz, modelnumber Y-RBA97) +# Note this one uses non-standard codes for FN + F9 - Fn + F12? +evdev:input:b0003v046Dp0066* + KEYBOARD_KEY_c100e=prog4 # Smartkey D (Fn + F12) → XF86Launch4 + KEYBOARD_KEY_c1019=prog1 # Smartkey A (Fn + F9) → XF86Launch1 + KEYBOARD_KEY_c101a=prog2 # Smartkey B (Fn + F10) → XF86Launch2 + KEYBOARD_KEY_c101b=prog3 # Smartkey C (Fn + F11) → XF86Launch3 + KEYBOARD_KEY_c1041=wordprocessor # Word icon (Fn + F1) + KEYBOARD_KEY_c1042=spreadsheet # Excel icon (Fn + F2) + KEYBOARD_KEY_c1043=presentation # Presentation icon (Fn + F3) + KEYBOARD_KEY_c1044=calendar # Calendar icon (Fn + F4) + KEYBOARD_KEY_c1045=homepage # Home icon (Fn + F5) + KEYBOARD_KEY_c1046=email # Letter icon (Fn + F6) + KEYBOARD_KEY_c1047=search # Magnifying glass icon (Fn + F7) + KEYBOARD_KEY_c1048=config # Window with gear icon (Fn + F8) + KEYBOARD_KEY_c106f=battery # Battery icon + +# S510 remote control (27 MHz) +evdev:input:b0003v046Dp00FE* + KEYBOARD_KEY_c1018=media # Media button + +# MX5000 keyboard (HID proxy mode and bluetooth matches) +# The 4 buttons below the LCD send codes 0xc100c - 0xc100f. They only send +# these codes when the LCD is displaying text send to it. These codes are +# directly consumed by recent versions of lcdproc when it is driving the LCD, +# so these codes should not be mapped +evdev:input:b0003v046DpB305* +evdev:input:b0005v046DpB305* + KEYBOARD_KEY_c0230=zoomreset # HUT says fullscreen, kbd says 100% + KEYBOARD_KEY_c1004=send # Send and receive / sync button + KEYBOARD_KEY_c1006=coffee # Status (online/away) button + KEYBOARD_KEY_c1007=camera # Webcam button + KEYBOARD_KEY_c1038=prog1 # Smartkey A → XF86Launch1 + KEYBOARD_KEY_c1039=prog2 # Smartkey B → XF86Launch2 + KEYBOARD_KEY_c103a=prog3 # Smartkey C → XF86Launch3 + KEYBOARD_KEY_c103b=prog4 # Smartkey D → XF86Launch4 + +# MX5500 keyboard (HID proxy mode and bluetooth matches) +evdev:input:b0003v046DpB30B* +evdev:input:b0005v046DpB30B* + KEYBOARD_KEY_c0183=media # HUT says consumer control configuration, kbd says Media Center + KEYBOARD_KEY_c100e=images # Camera icon, "Photo Gallery" + KEYBOARD_KEY_c100f=config # Window with gear icon + KEYBOARD_KEY_c1038=prog1 # Smartkey A → XF86Launch1 + KEYBOARD_KEY_c1039=prog2 # Smartkey B → XF86Launch2 + KEYBOARD_KEY_c103a=prog3 # Smartkey C → XF86Launch3 + KEYBOARD_KEY_c103b=prog4 # Smartkey D → XF86Launch4 + # iTouch evdev:input:b0003v046DpC308* KEYBOARD_KEY_90001=shop # Shopping @@ -923,8 +1089,8 @@ evdev:input:b0003v046DpC309* KEYBOARD_KEY_c0207=save # Save (F8) KEYBOARD_KEY_c0194=file # My Computer (F9) KEYBOARD_KEY_c01a7=documents # My Documents (F10) - KEYBOARD_KEY_c01b6=images # My Pictures (F11) ?? - KEYBOARD_KEY_c01b7=sound # My Music (F12) ?? + KEYBOARD_KEY_c01b6=images # My Pictures (F11) + KEYBOARD_KEY_c01b7=audio # My Music (F12) ########################################################### diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb index 11d5e8349d..64dc28bb46 100644 --- a/hwdb/60-sensor.hwdb +++ b/hwdb/60-sensor.hwdb @@ -57,6 +57,12 @@ # automatically flip their output for an upside-down display when the device # is held upright. # +# ACCEL_LOCATION=<location> +# +# where <location> is the location of the sensor. This value could be 'base' +# or 'display'. The default, when unset, is equivalent to: +# ACCEL_LOCATION=display +# # Sort by brand, model ######################################### @@ -160,7 +166,7 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo:pnP02BD6_HI-122LP:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 # Chuwi Hi13 -sensor:modalias:acpi:KIOX000A*:dmi:svnChuwi*:pnHi13 +sensor:modalias:acpi:KIOX000A*:dmi:*:svnChuwi*:pnHi13:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 # Chuwi HiBook @@ -371,6 +377,10 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnMEDION:pnAkoyaE2212TMD99720:* sensor:modalias:acpi:KIOX010A*:dmi:*:svnMEDION*:pnE3216*:* ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1 +# Medion Akoya E3222 MD62450 +sensor:modalias:acpi:KIOX010A*:dmi:*:svnMEDION*:pnE3222*:* + ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1 + ######################################### # MSI ######################################### diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 553465a87e..92d079f22f 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -60,7 +60,7 @@ # Trackpoint const accel settings are specified as # POINTINGSTICK_CONST_ACCEL=<accel> # -# Where <accel> is a floating point number, using a '.' seperator, specifying +# Where <accel> is a floating point number, using a '.' separator, specifying # by how much to multiply deltas generated by the pointingstick to get # normalized deltas. # diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py index d84fba2221..fcc9a65e2b 100755 --- a/hwdb/parse_hwdb.py +++ b/hwdb/parse_hwdb.py @@ -126,6 +126,7 @@ def property_grammar(): ('KEYBOARD_LED_NUMLOCK', Literal('0')), ('KEYBOARD_LED_CAPSLOCK', Literal('0')), ('ACCEL_MOUNT_MATRIX', mount_matrix), + ('ACCEL_LOCATION', STRING), ) fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE') for name, val in props] @@ -177,6 +178,10 @@ def check_one_default(prop, settings): if len(defaults) > 1: error('More than one star entry: {!r}', prop) +def check_one_accel_location(prop, value): + if value not in ['base', 'display']: + error('Wrong accel location: {!r}', prop) + def check_one_mount_matrix(prop, value): numbers = [s for s in value if s not in {';', ','}] if len(numbers) != 9: @@ -219,6 +224,8 @@ def check_properties(groups): check_one_default(prop, parsed.VALUE.SETTINGS) elif parsed.NAME == 'ACCEL_MOUNT_MATRIX': check_one_mount_matrix(prop, parsed.VALUE) + elif parsed.NAME == 'ACCEL_LOCATION': + check_one_accel_location(prop, parsed.VALUE) elif parsed.NAME.startswith('KEYBOARD_KEY_'): check_one_keycode(prop, parsed.VALUE) diff --git a/man/bootup.xml b/man/bootup.xml index 9468a61319..d371523400 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -156,7 +156,9 @@ using systemd as well. In this case, boot up inside the initrd follows the following structure.</para> - <para>The default target in the initrd is + <para>systemd detects that it is run within an initrd by checking + for the file <filename>/etc/initrd-release</filename>. + The default target in the initrd is <filename>initrd.target</filename>. The bootup process begins identical to the system manager bootup (see above) until it reaches <filename>basic.target</filename>. From there, systemd diff --git a/man/journalctl.xml b/man/journalctl.xml index a3c67f5e82..0ecab521fa 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -887,18 +887,34 @@ <varlistentry> <term><option>--flush</option></term> - <listitem><para>Asks the journal daemon to flush any log data - stored in <filename>/run/log/journal</filename> into - <filename>/var/log/journal</filename>, if persistent storage - is enabled. This call does not return until the operation is - complete. Note that this call is idempotent: the data is only - flushed from <filename>/run/log/journal</filename> into - <filename>/var/log/journal</filename> once during system - runtime, and this command exits cleanly without executing any - operation if this has already happened. This command - effectively guarantees that all data is flushed to - <filename>/var/log/journal</filename> at the time it - returns.</para></listitem> + <listitem><para>Asks the journal daemon to flush any log data stored in + <filename>/run/log/journal/</filename> into <filename>/var/log/journal/</filename>, if persistent + storage is enabled. This call does not return until the operation is complete. Note that this call is + idempotent: the data is only flushed from <filename>/run/log/journal/</filename> into + <filename>/var/log/journal</filename> once during system runtime (but see + <option>--relinquish-var</option> below), and this command exits cleanly without executing any + operation if this has already happened. This command effectively guarantees that all data is flushed + to <filename>/var/log/journal</filename> at the time it returns.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--relinquish-var</option></term> + + <listitem><para>Asks the journal daemon for the reverse operation to <option>--flush</option>: if + requested the daemon will write further log data to <filename>/run/log/journal/</filename> and stops + writing to <filename>/var/log/journal/</filename>. A subsequent call to <option>--flush</option> + causes the log output to switch back to <filename>/var/log/journal/</filename>, see + above.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--smart-relinquish-var</option></term> + + <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root file + system and <filename>/var/lib/journal/</filename> reside on the same mount point. This operation is + used during system shutdown in order to make the journal daemon stop writing data to + <filename>/var/log/journal/</filename> in case that directory is located on a mount point that needs + to be unmounted.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/resolvectl.xml b/man/resolvectl.xml index f986e98ba3..d4c04a1e32 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -255,7 +255,7 @@ through external means. The <option>dns</option> command expects IPv4 or IPv6 address specifications of DNS servers to use. The <option>domain</option> command expects valid DNS domains, possibly prefixed with <literal>~</literal>, and configures a per-interface search or route-only domain. The - <option>default-route</option> command expects a boolean paremeter, and configures whether the link may be + <option>default-route</option> command expects a boolean parameter, and configures whether the link may be used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no other link explicitly is configured for. The <option>llmnr</option>, <option>mdns</option>, <option>dnssec</option> and <option>dnsovertls</option> commands may be used to configure the per-interface LLMNR, MulticastDNS, DNSSEC diff --git a/man/rules/meson.build b/man/rules/meson.build index 6894158466..6d47f09f48 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -445,7 +445,10 @@ manpages = [ ['sd_event_source_set_userdata', '3', ['sd_event_source_get_userdata'], ''], ['sd_event_source_unref', '3', - ['sd_event_source_ref', 'sd_event_source_unrefp'], + ['sd_event_source_disable_unref', + 'sd_event_source_disable_unrefp', + 'sd_event_source_ref', + 'sd_event_source_unrefp'], ''], ['sd_event_wait', '3', @@ -824,6 +827,7 @@ manpages = [ ['systemd.kill', '5', [], ''], ['systemd.link', '5', [], ''], ['systemd.mount', '5', [], ''], + ['systemd.net-naming-scheme', '7', [], ''], ['systemd.netdev', '5', [], 'ENABLE_NETWORKD'], ['systemd.network', '5', [], 'ENABLE_NETWORKD'], ['systemd.nspawn', '5', [], ''], diff --git a/man/sd_bus_add_object_vtable.xml b/man/sd_bus_add_object_vtable.xml index 92be236afd..6cbb84e7ff 100644 --- a/man/sd_bus_add_object_vtable.xml +++ b/man/sd_bus_add_object_vtable.xml @@ -277,7 +277,7 @@ </para> <para>Equivalent to <constant>SD_BUS_SIGNAL_WITH_NAMES()</constant> with the - <replaceable>names</replaceable> paramater unset (i.e. no parameter names).</para> + <replaceable>names</replaceable> parameter unset (i.e. no parameter names).</para> </listitem> </varlistentry> diff --git a/man/sd_bus_close.xml b/man/sd_bus_close.xml index b09f488874..d81c593878 100644 --- a/man/sd_bus_close.xml +++ b/man/sd_bus_close.xml @@ -44,7 +44,7 @@ <para><function>sd_bus_close()</function> disconnects the specified bus connection. When this call is invoked and the specified bus object refers to an active connection it is immediately terminated. No further messages may be - sent or receieved on it. Any messages queued in the bus object (both incoming and outgoing) are released. If + sent or received on it. Any messages queued in the bus object (both incoming and outgoing) are released. If invoked on <constant>NULL</constant> bus object or when the bus connection is already closed this function executes no operation. This call does not free or unreference the bus object itself. Use <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> for that.</para> diff --git a/man/sd_bus_message_new_method_error.xml b/man/sd_bus_message_new_method_error.xml index 27cec8ed01..0c471c534f 100644 --- a/man/sd_bus_message_new_method_error.xml +++ b/man/sd_bus_message_new_method_error.xml @@ -116,7 +116,7 @@ project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry> format string <parameter>format</parameter> and corresponding arguments. <literal>%m</literal> may be used in the format string to refer to the error - string corresponding to the specified errno code. The error message is initalized + string corresponding to the specified errno code. The error message is initialized using the error identifier generated from <constant>error</constant> and the formatted string. (If <parameter>error</parameter> is zero, no error is actually set, and an error reply with no information is created.)</para> diff --git a/man/sd_bus_message_rewind.xml b/man/sd_bus_message_rewind.xml index aa8aea987b..cbfa2511d2 100644 --- a/man/sd_bus_message_rewind.xml +++ b/man/sd_bus_message_rewind.xml @@ -19,7 +19,7 @@ <refnamediv> <refname>sd_bus_message_rewind</refname> - <refpurpose>Return to begining of message or current container</refpurpose> + <refpurpose>Return to beginning of message or current container</refpurpose> </refnamediv> <refsynopsisdiv> @@ -38,7 +38,7 @@ <title>Description</title> <para><function>sd_bus_message_rewind()</function> moves the "read pointer" in the message - <parameter>m</parameter> to either the begining of the message (if + <parameter>m</parameter> to either the beginning of the message (if <parameter>complete</parameter> is true) or to the beginning of the currently open container. If no container is open, <parameter>complete</parameter> has no effect.</para> </refsect1> diff --git a/man/sd_bus_message_verify_type.xml b/man/sd_bus_message_verify_type.xml index c3230e5833..e03a253885 100644 --- a/man/sd_bus_message_verify_type.xml +++ b/man/sd_bus_message_verify_type.xml @@ -70,7 +70,7 @@ <listitem><para><parameter>m</parameter> or both <parameter>type</parameter> and <parameter>contents</parameter> are <constant>NULL</constant>.</para> - <para>Arguments do not satisfy other contraints listed above.</para> + <para>Arguments do not satisfy other constraints listed above.</para> </listitem> </varlistentry> diff --git a/man/sd_bus_set_description.xml b/man/sd_bus_set_description.xml index cfcebdfb29..3c5580e27c 100644 --- a/man/sd_bus_set_description.xml +++ b/man/sd_bus_set_description.xml @@ -130,33 +130,25 @@ <listitem><para>An argument is invalid.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-ENOPKG</constant></term> <listitem><para>The bus cannot be resolved.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-EPERM</constant></term> <listitem><para>The bus has already been started.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-ECHILD</constant></term> <listitem><para>The bus was created in a different process.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-ENOMEM</constant></term> diff --git a/man/sd_bus_slot_set_description.xml b/man/sd_bus_slot_set_description.xml index 13dd6f8815..9bc2ba8592 100644 --- a/man/sd_bus_slot_set_description.xml +++ b/man/sd_bus_slot_set_description.xml @@ -72,17 +72,13 @@ <listitem><para>An required argument is <constant>NULL</constant>.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-ENXIO</constant></term> <listitem><para>The bus slot object has no description.</para></listitem> </varlistentry> - </variablelist> - <variablelist> <varlistentry> <term><constant>-ENOMEM</constant></term> diff --git a/man/sd_event_source_unref.xml b/man/sd_event_source_unref.xml index 01e3008eed..81131fa737 100644 --- a/man/sd_event_source_unref.xml +++ b/man/sd_event_source_unref.xml @@ -19,6 +19,8 @@ <refname>sd_event_source_unref</refname> <refname>sd_event_source_unrefp</refname> <refname>sd_event_source_ref</refname> + <refname>sd_event_source_disable_unref</refname> + <refname>sd_event_source_disable_unrefp</refname> <refpurpose>Increase or decrease event source reference counters</refpurpose> </refnamediv> @@ -42,6 +44,15 @@ <paramdef>sd_event_source *<parameter>source</parameter></paramdef> </funcprototype> + <funcprototype> + <funcdef>sd_event_source* <function>sd_event_source_disable_unref</function></funcdef> + <paramdef>sd_event_source *<parameter>source</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>void <function>sd_event_source_disable_unrefp</function></funcdef> + <paramdef>sd_event_source **<parameter>source</parameter></paramdef> + </funcprototype> </funcsynopsis> </refsynopsisdiv> @@ -77,23 +88,32 @@ the passed event source object is <constant>NULL</constant>.</para> - <para>Note that event source objects stay alive and may be - dispatched as long as they have a reference counter greater than - zero. In order to drop a reference of an event source and make - sure the associated event source handler function is not called - anymore it is recommended to combine a call of + <para>Note that event source objects stay alive and may be dispatched as long as they have a reference + counter greater than zero. In order to drop a reference of an event source and make sure the associated + event source handler function is not called anymore it is recommended to combine a call of <function>sd_event_source_unref()</function> with a prior call to - <function>sd_event_source_set_enabled()</function> with - <constant>SD_EVENT_OFF</constant>.</para> + <function>sd_event_source_set_enabled()</function> with <constant>SD_EVENT_OFF</constant> or call + <function>sd_event_source_disable_unref()</function>, see below.</para> + + <para><function>sd_event_source_disable_unref()</function> combines a call to + <function>sd_event_source_set_enabled()</function> with <constant>SD_EVENT_OFF</constant> with + <function>sd_event_source_unref()</function>. This ensures that the source is disabled before the local + reference to it is lost. The <parameter>source</parameter> parameter is allowed to be + <constant>NULL</constant>.</para> + + <para><function>sd_event_source_disable_unrefp()</function> is similar to + <function>sd_event_source_unrefp()</function>, but in addition disables the source first. This call is + useful in conjunction with GCC's and LLVM's + <ulink url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up Variable + Attribute</ulink>. Note that this function is defined as inline function.</para> </refsect1> <refsect1> <title>Return Value</title> - <para><function>sd_event_source_unref()</function> always returns - <constant>NULL</constant>. - <function>sd_event_source_ref()</function> always returns the - event source object passed in.</para> + <para><function>sd_event_source_unref()</function> and + <function>sd_event_source_disable_unref()</function> always return <constant>NULL</constant>. + <function>sd_event_source_ref()</function> always returns the event source object passed in.</para> </refsect1> <xi:include href="libsystemd-pkgconfig.xml" /> diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index f559b858f9..abc05e9303 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -337,7 +337,7 @@ $ eog targets.svg</programlisting> </example> <para>Note that this verb prints the list that is compiled into <command>systemd-analyze</command> - itself, and does not comunicate with the running manager. Use + itself, and does not communicate with the running manager. Use <programlisting>systemctl [--user] [--global] show -p UnitPath --value</programlisting> to retrieve the actual list that the manager uses, with any empty directories omitted.</para> </refsect2> diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 3b1319687f..2575ab3fe5 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -357,7 +357,7 @@ <para><command>systemd-boot</command> implements a simple boot counting mechanism on top of the <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, for automatic and unattended - fallback to older kernel versions/boot loader entries when a specific entry continously fails. Any boot loader + fallback to older kernel versions/boot loader entries when a specific entry continuously fails. Any boot loader entry file and unified kernel image file that contains a <literal>+</literal> followed by one or two numbers (if two they need to be separated by a <literal>-</literal>), before the <filename>.conf</filename> or <filename>.efi</filename> suffix is subject to boot counting: the first of the two numbers ('tries left') is diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 28d997cfa9..d599ac20f1 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -62,7 +62,7 @@ </thead> <tbody> <row> - <entry valign="top" morerows="11">VM</entry> + <entry valign="top" morerows="12">VM</entry> <entry><varname>qemu</varname></entry> <entry>QEMU software virtualization, without KVM</entry> </row> @@ -128,7 +128,7 @@ </row> <row> - <entry valign="top" morerows="6">Container</entry> + <entry valign="top" morerows="7">Container</entry> <entry><varname>openvz</varname></entry> <entry>OpenVZ/Virtuozzo</entry> </row> @@ -154,6 +154,11 @@ </row> <row> + <entry><varname>podman</varname></entry> + <entry><ulink url="https://podman.io">Podman</ulink> container manager</entry> + </row> + + <row> <entry><varname>rkt</varname></entry> <entry>rkt app container runtime</entry> </row> diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index 0d6d4e307e..d94d6ac715 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -234,7 +234,7 @@ <term><varname>root=</varname></term> <listitem><para>When used with the special value <literal>gpt-auto</literal>, automatic discovery of - the root parition based on the GPT partition type is enabled. Any other value disables this + the root partition based on the GPT partition type is enabled. Any other value disables this generator.</para></listitem> </varlistentry> diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml index 7c82f68fb3..51b865dc0b 100644 --- a/man/systemd-networkd-wait-online.service.xml +++ b/man/systemd-networkd-wait-online.service.xml @@ -55,7 +55,7 @@ one is necessary to access some network resources. When used, all other interfaces are ignored. This option may be used more than once to wait for multiple network interfaces. When this option is specified multiple times, then <command>systemd-networkd-wait-online</command> waits - for all specified interfaces to be online. Optinally, required minimum operational state can be + for all specified interfaces to be online. Optionally, required minimum operational state can be specified after a colon <literal>:</literal>. Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for possible operational states. If the operational state is not specified here, then diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 807c3238b8..53c46a1018 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -163,7 +163,7 @@ <listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.</para></listitem> </itemizedlist> - <para>The "DNS default route" option is a boolean setting configureable with <command>resolvectl</command> or in + <para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command> or in <filename>.network</filename> files. If not set, it is implicitly determined based on the configured DNS domains for a link: if there's any route-only domain (not matching <literal>~.</literal>) it defaults to false, otherwise to true.</para> diff --git a/man/systemd-udev-settle.service.xml b/man/systemd-udev-settle.service.xml index 6fbd99111f..3698bfaf19 100644 --- a/man/systemd-udev-settle.service.xml +++ b/man/systemd-udev-settle.service.xml @@ -33,7 +33,7 @@ <para><emphasis>Using this service is not recommended.</emphasis> There can be no guarantee that hardware is fully discovered at any specific time, because the kernel does hardware detection asynchronously, and - certain busses and devices take a very long time to become ready, and also additional hardware may be + certain buses and devices take a very long time to become ready, and also additional hardware may be plugged in at any time. Instead, services should subscribe to udev events and react to any new hardware as it is discovered. Services that, based on configuration, expect certain devices to appear, may warn or report failure after a timeout. This timeout should be tailored to the hardware type. Waiting for diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index cf8087ccb3..c267bb2b11 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -171,15 +171,12 @@ <term><varname>net.naming-scheme=</varname></term> <listitem> <para>Network interfaces are renamed to give them predictable names when possible (unless - <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various - device metadata fields. Newer versions of <filename>systemd-udevd.service</filename> take more of - these fields into account, improving (and thus possibly changing) the names used for the same - devices. With this kernel command line option it is possible to pick a specific version of this - algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers - are known: <literal>v238</literal>, <literal>v239</literal>, <literal>v240</literal> which each - implement the naming scheme that was the default in the indicated systemd version. In addition, - <literal>latest</literal> may be used to designate the latest scheme known (to this particular - version of <filename>systemd-udevd.service</filename>).</para> + <varname>net.ifnames=0</varname> is specified, see above). With this kernel command line option it + is possible to pick a specific version of this algorithm and override the default chosen at + compilation time. Expects one of the naming scheme identifiers listed in + <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + or <literal>latest</literal> to select the latest scheme known (to this particular version of + <filename>systemd-udevd.service</filename>).</para> <para>Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the naming is generally derived from driver attributes exposed by the kernel. As the kernel is @@ -188,9 +185,8 @@ </listitem> </varlistentry> </variablelist> - <!-- when adding entries here, consider also adding them - in kernel-command-line.xml --> - </refsect1> + <!-- when adding entries here, consider also adding them in kernel-command-line.xml --> + </refsect1> <refsect1> <title>See Also</title> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index b69691b3c4..719c2c5ff3 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1304,7 +1304,7 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting> running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. By default, no restrictions apply, all address families are accessible to processes. If assigned the empty string, any - previous address familiy restriction changes are undone. This setting does not affect commands prefixed with + previous address family restriction changes are undone. This setting does not affect commands prefixed with <literal>+</literal>.</para> <para>Use this option to limit exposure of processes to remote access, in particular via exotic and sensitive @@ -1928,7 +1928,7 @@ SystemCallErrorNumber=EPERM</programlisting> <para>Note that services which specify <option>DefaultDependencies=no</option> and use <varname>StandardInput=</varname> or <varname>StandardOutput=</varname> with <option>tty</option>/<option>tty-force</option>/<option>tty-fail</option>, should specify - <option>After=systemd-vconsole-setup.service</option>, to make sure that the tty intialization is + <option>After=systemd-vconsole-setup.service</option>, to make sure that the tty initialization is finished before they start.</para></listitem> </varlistentry> diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index 960b2ec633..fa374bf0f8 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -104,6 +104,11 @@ usually derived from glibc's <varname>program_invocation_short_name</varname> variable, see <citerefentry project='die-net'><refentrytitle>program_invocation_short_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>.)</para> + <para>Note that the journal service does not validate the values of any structured + journal fields whose name is not prefixed with an underscore, and this includes any + syslog related fields such as these. Hence, applications that supply a facility, PID, + or log level are expected to do so properly formatted, i.e. as numeric integers formatted + as decimal strings.</para> </listitem> </varlistentry> diff --git a/man/systemd.link.xml b/man/systemd.link.xml index af9799e8c0..b1be32955e 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -60,8 +60,13 @@ <refsect1> <title>[Match] Section Options</title> - <para>A link file is said to match a device if each of the entries in the [Match] section matches, or if - the section is empty. The following keys are accepted:</para> + <para>A link file is said to match a device if all matches specified by the + <literal>[Match]</literal> section are satisfied. When a link file does not contain valid settings + in <literal>[Match]</literal> section, then the file will match all devices and + <command>systemd-udevd</command> warns about that. Hint: to avoid the warning and to make it clear + that all interfaces shall be matched, add the following: + <programlisting>OriginalName=*</programlisting> + The following keys are accepted:</para> <variablelist class='network-directives'> <varlistentry> @@ -273,6 +278,7 @@ <para>The name is set based on information given by the firmware for on-board devices, as exported by the udev property <varname>ID_NET_NAME_ONBOARD</varname>. + See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>. </para> </listitem> </varlistentry> @@ -282,6 +288,7 @@ <para>The name is set based on information given by the firmware for hot-plug devices, as exported by the udev property <varname>ID_NET_NAME_SLOT</varname>. + See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>. </para> </listitem> </varlistentry> @@ -290,7 +297,9 @@ <listitem> <para>The name is set based on the device's physical location, as exported by the udev property - <varname>ID_NET_NAME_PATH</varname>.</para> + <varname>ID_NET_NAME_PATH</varname>. + See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>. + </para> </listitem> </varlistentry> <varlistentry> @@ -298,7 +307,9 @@ <listitem> <para>The name is set based on the device's persistent MAC address, as exported by the udev property - <varname>ID_NET_NAME_MAC</varname>.</para> + <varname>ID_NET_NAME_MAC</varname>. + See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>. + </para> </listitem> </varlistentry> <varlistentry> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index d0ccd39e38..022e227e83 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -312,7 +312,7 @@ <listitem><para>The file system will be initialized on the device. If the device is not "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected that this option - remains set even after the device has been initalized.</para> + remains set even after the device has been initialized.</para> <para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored when part of the diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml new file mode 100644 index 0000000000..eb8faad254 --- /dev/null +++ b/man/systemd.net-naming-scheme.xml @@ -0,0 +1,420 @@ +<?xml version='1.0'?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<!-- SPDX-License-Identifier: LGPL-2.1+ --> + +<refentry id="systemd.net-naming-scheme"> + <refentryinfo> + <title>systemd.net-naming-scheme</title> + <productname>systemd</productname> + </refentryinfo> + + <refmeta> + <refentrytitle>systemd.net-naming-scheme</refentrytitle> + <manvolnum>7</manvolnum> + </refmeta> + + <refnamediv> + <refname>systemd.net-naming-scheme</refname> + <refpurpose>Network device naming schemes</refpurpose> + </refnamediv> + + <refsect1> + <title>Description</title> + + <para>Network interfaces may be renamed to give them predictable names when there's enough information to + generate appropriate names and the use of certain types of names is configured. This page describes the + first part, i.e. what possible names may be generated. Those names are generated by the + <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> + builtin <command>net_id</command> and exported as udev properties + (<varname>ID_NET_NAME_ONBOARD=</varname>, <varname>ID_NET_LABEL_ONBOARD=</varname>, + <varname>ID_NET_NAME_PATH=</varname>, <varname>ID_NET_NAME_SLOT=</varname>).</para> + + <para>Names are derived from various device metadata attributes. Newer versions of udev take more of + these attributes into account, improving (and thus possibly changing) the names used for the same + devices. Differents version of the naming rules are called "naming schemes". The default naming scheme is + chosen at compilation time. Usually this will be the latest implemented version, but it is also possible + to set one of the older versions to preserve compatibility. This may be useful for example for + distributions, which may introduce new versions of systemd in stable releases without changing the naming + scheme. The naming scheme may also be overriden using the <varname>net.naming-scheme=</varname> kernel + command line switch, see + <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. + Available naming schemes are described below.</para> + + <para>After the udev proprties have been generated, appropriate udev rules may be used to actually rename + devices based on those properties. See the description of <varname>NamePolicy=</varname> in + <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para> + </refsect1> + + <refsect1> + <title>Naming</title> + + <para>All names start with a two-character prefix that signifies the interface type.</para> + + <table> + <title>Two character prefixes based on the type of interface</title> + + <tgroup cols='2'> + <thead> + <row> + <entry>Prefix</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry><constant>en</constant></entry> + <entry>Ethernet</entry> + </row> + <row> + <entry><constant>ib</constant></entry> + <entry>InfiniBand</entry> + </row> + <row> + <entry><constant>sl</constant></entry> + <entry>serial line IP (slip)</entry> + </row> + <row> + <entry><constant>wl</constant></entry> + <entry>Wireless local area network (WLAN)</entry> + </row> + <row> + <entry><constant>ww</constant></entry> + <entry>Wireless wide area network (WWAN)</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>The udev <command>net_id</command> builtin exports the following udev device properties:</para> + + <variablelist> + <varlistentry> + <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></varname></term> + + <listitem><para>This name is set based on the ordering information given by the firmware for + on-board devices. The name consists of the prefix, letter <constant>o</constant>, and a number + specified by the firmware. This is only available for PCI devices.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>ID_NET_LABEL_ONBOARD=<replaceable>prefix</replaceable> <replaceable>label</replaceable></varname></term> + + <listitem><para>This property is set based on label given by the firmware for on-board devices. The + name consists of the prefix concatenated with the label. This is only available for PCI devices. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>ID_NET_NAME_MAC=<replaceable>prefix</replaceable><constant>x</constant><replaceable>AABBCCDDEEFF</replaceable></varname></term> + + <listitem><para>This name consists of the prefix, letter <constant>x</constant>, and 12 hexadecimal + digits of the MAC address. It is available if the device has a fixed MAC address. Because this name + is based on an attribute of the card itself, it remains "stable" when the device is moved (even + between machines), but will change when the hardware is replaced.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]</varname></term> + <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>b</constant><replaceable>number</replaceable></varname></term> + <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</varname></term> + <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>v</constant><replaceable>slot</replaceable></varname></term> + + <listitem><para>This property describes the slot position. Different schemes are used depending on + the bus type, as described in the table below. In all cases, PCI slot information must be known. In + case of USB, BCMA, and SR-VIO devices, the full name consists of the prefix, PCI slot identifier, + and USB or BCMA or SR-VIO slot identifier. The first two parts are denoted as "…" in the table + below.</para> + + <table> + <title>Slot naming schemes</title> + + <tgroup cols='2'> + <thead> + <row> + <entry>Format</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><replaceable>prefix</replaceable> [<constant>P</constant><replaceable>domain</replaceable>] <constant>s</constant><replaceable>slot</replaceable> [<constant>f</constant><replaceable>function</replaceable>] [<constant>n</constant><replaceable>port_name</replaceable> | <constant>d</constant><replaceable>dev_port</replaceable>]</entry> + <entry>PCI slot number</entry> + </row> + + <row> + <entry>… <constant>b</constant><replaceable>number</replaceable></entry> + <entry>Broadcom bus (BCMA) core number</entry> + </row> + + <row> + <entry>… <constant>u</constant><replaceable>port</replaceable>… [<constant>c</constant><replaceable>config</replaceable>] [<constant>i</constant><replaceable>interface</replaceable>]</entry> + <entry>USB port number chain</entry> + </row> + + <row> + <entry>… <constant>v</constant><replaceable>slot</replaceable></entry> + <entry>SR-VIO slot number</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>The PCI domain is only prepended when it is not 0. All multi-function PCI devices will carry + the <constant>f<replaceable>function</replaceable></constant> number in the device name, including + the function 0 device. For non-multi-function devices, the number is suppressed if 0. The port name + <replaceable>port_name</replaceable> is used, or the port number + <constant>d</constant><replaceable>dev_port</replaceable> if the name is not known.</para> + + <para>For BCMA devices, the core number is suppressed when 0.</para> + + <para>For USB devices the full chain of port numbers of hubs is composed. If the name gets longer + than the maximum number of 15 characters, the name is not exported. The usual USB configuration + number 1 and interface number 0 values are suppressed.</para> + </listitem> + + <para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of + <constant>v</constant> and the virtual device number, with any leading zeros removed. The bus + number is ignored. This device type is found in IBM PowerVMs.</para> + </varlistentry> + + <varlistentry> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>c</constant><replaceable>bus_id</replaceable></varname></term> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>a</constant><replaceable>vendor</replaceable><replaceable>model</replaceable><constant>i</constant><replaceable>instance</replaceable></varname></term> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>i</constant><replaceable>address</replaceable><constant>n</constant><replaceable>port_name</replaceable></varname></term> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]</varname></term> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>b</constant><replaceable>number</replaceable></varname></term> + <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</varname></term> + + <listitem><para>This property describes the device installation location. Different schemes are + used depending on the bus type, as described in the table below. For BCMA and USB devices, PCI path + information must known, and the full name consists of the prefix, PCI slot identifier, and USB or + BCMA location. The first two parts are denoted as "…" in the table below.</para> + + <table> + <title>Path naming schemes</title> + + <tgroup cols='2'> + <thead> + <row> + <entry>Format</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><replaceable>prefix</replaceable> <constant>c</constant><replaceable>bus_id</replaceable></entry> + <entry>CCW or grouped CCW device identifier</entry> + </row> + + <row> + <entry><replaceable>prefix</replaceable> <constant>a</constant><replaceable>vendor</replaceable> <replaceable>model</replaceable> <constant>i</constant><replaceable>instance</replaceable></entry> + <entry>ACPI path names for ARM64 platform devices</entry> + </row> + + <row> + <entry><replaceable>prefix</replaceable> <constant>i</constant><replaceable>address</replaceable> <constant>n</constant><replaceable>port_name</replaceable></entry> + <entry>Netdevsim (simulated networking device) device number and port name</entry> + </row> + + <row> + <entry><replaceable>prefix</replaceable> [<constant>P</constant><replaceable>domain</replaceable>] <constant>p</constant><replaceable>bus</replaceable> <constant>s</constant><replaceable>slot</replaceable> [<constant>f</constant><replaceable>function</replaceable>] [<constant>n</constant><replaceable>phys_port_name</replaceable> | <constant>d</constant><replaceable>dev_port</replaceable>]</entry> + <entry>PCI geographical location</entry> + </row> + + <row> + <entry>… <constant>b</constant><replaceable>number</replaceable></entry> + <entry>Broadcom bus (BCMA) core number</entry> + </row> + + <row> + <entry>… <constant>u</constant><replaceable>port</replaceable>… [<constant>c</constant><replaceable>config</replaceable>] [<constant>i</constant><replaceable>interface</replaceable>]</entry> + <entry>USB port number chain</entry> + </row> + + </tbody> + </tgroup> + </table> + + <para>CCW and grouped CCW devices are found in IBM System Z mainframes. Any leading zeros and + dots are suppressed.</para> + + <para>For PCI, BCMA, and USB devices, the same rules as described above for slot naming are + used.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>History</title> + + <para>The following "naming schemes" have been defined:</para> + + <variablelist> + <varlistentry> + <term><constant>v238</constant></term> + + <listitem><para>This is the naming naming that was implemented in systemd 238.</para></listitem> + </varlistentry> + + <varlistentry> + <term><constant>v239</constant></term> + + <listitem><para>Naming was changed for virtual network interfaces created with SR-IOV and NPAR and + for devices where the PCI network controller device does not have a slot number associated.</para> + + <para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of + <literal>v<replaceable>port</replaceable></literal>, where <replaceable>port</replaceable> is the + virtual device number. Previously those virtual devices were named as if completely independent. + </para> + + <para>The ninth and later NPAR virtual devices are named following the scheme used for the first + eight NPAR partitions. Previously those devices were not renamed and the kernel default + ("eth<replaceable>N</replaceable>") was used.</para> + + <para>Names are also generated for PCI devices where the PCI network controller device does not + have an associated slot number itself, but one of its parents does. Previously those devices were + not renamed and the kernel default was used.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><constant>v240</constant></term> + + <para>The <literal>ib</literal> prefix and stable names for infiniband devices are + introduced. Previously those devices were not renamed.</para> + + <para>The ACPI index field (used in <varname>ID_NET_NAME_ONBOARD=</varname>) is now also used when + 0.</para> + + <para>A new naming policy <varname>NamePolicy=keep</varname> was introduced. With this policy, if + the network device name was already set by userspace, the device will not be renamed + again. Previously, this naming policy applied implicitly, and now it must be explicitly + requested. Effectively, this means that network devices will be renamed according to the + configuration, even if they have been renamed already, if <constant>keep</constant> is not + specified as the naming policy in the <filename noindex='true'>.link</filename> file. See + <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for a description of <varname>NamePolicy=</varname>.</para> + </varlistentry> + + <varlistentry> + <term><constant>v243</constant></term> + + <para>Support for netdevsim (simulated networking devices) was added. Previously those devices were + not renamed.</para> + + <para>Previously two-letter interface type prefix was prepended to + <varname>ID_NET_LABEL_ONBOARD=</varname>. This is not done anymore.</para> + </varlistentry> + + <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this + particular version of systemd.</para> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <example> + <title>Using <command>udevadm test-builtin</command> to display device properties</title> + + <programlisting>$ udevadm test-builtin net_id /sys/class/net/enp0s31f6 +... +Using default interface naming scheme 'v243'. +ID_NET_NAMING_SCHEME=v243 +ID_NET_NAME_MAC=enx54ee75cb1dc0 +ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. +ID_NET_NAME_PATH=enp0s31f6 +...</programlisting> + </example> + + <example> + <title>PCI Ethernet card with firmware index "1"</title> + + <programlisting>ID_NET_NAME_ONBOARD=eno1 +ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1 + </programlisting> + </example> + + <example> + <title>PCI Ethernet card in hotplug slot with firmware index number</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1 +ID_NET_NAME_MAC=enx000000000466 +ID_NET_NAME_PATH=enp5s0 +ID_NET_NAME_SLOT=ens1</programlisting> + </example> + + <example> + <title>PCI Ethernet multi-function card with 2 ports</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 +ID_NET_NAME_MAC=enx78e7d1ea46da +ID_NET_NAME_PATH=enp2s0f0 + +# /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 +ID_NET_NAME_MAC=enx78e7d1ea46dc +ID_NET_NAME_PATH=enp2s0f1</programlisting> + </example> + + <example> + <title>PCI WLAN card</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 +ID_NET_NAME_MAC=wlx0024d7e31130 +ID_NET_NAME_PATH=wlp3s0</programlisting> + </example> + + <example> + <title>PCI IB host adapter with 2 ports</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.0/net/ibp21s0f0 +ID_NET_NAME_PATH=ibp21s0f0 + +# /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.1/net/ibp21s0f1 +ID_NET_NAME_PATH=ibp21s0f1</programlisting> + </example> + + <example> + <title>USB built-in 3G modem</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 +ID_NET_NAME_MAC=wwx028037ec0200 +ID_NET_NAME_PATH=wwp0s29u1u4i6</programlisting> + </example> + + <example> + <title>USB Android phone</title> + + <programlisting># /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 +ID_NET_NAME_MAC=enxd626b3450fb5 +ID_NET_NAME_PATH=enp0s29u1u2</programlisting> + </example> + + <example> + <title>s390 grouped CCW interface</title> + + <programlisting># /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 +ID_NET_NAME_MAC=enx026d3c00000a +ID_NET_NAME_PATH=encf5f0</programlisting> + </example> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>, + <ulink url="https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames">the + original page describing stable interface names</ulink> + </para> + </refsect1> + +</refentry> diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index a58de37b3c..0b0fff1f65 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -115,6 +115,9 @@ <row><entry><varname>ipvlan</varname></entry> <entry>An ipvlan device is a stacked device which receives packets from its underlying device based on IP address filtering.</entry></row> + <row><entry><varname>ipvtap</varname></entry> + <entry>An ipvtap device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface.</entry></row> + <row><entry><varname>macvlan</varname></entry> <entry>A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering.</entry></row> @@ -520,6 +523,15 @@ </refsect1> <refsect1> + <title>[IPVTAP] Section Options</title> + + <para>The <literal>[IPVTAP]</literal> section only applies for + netdevs of kind <literal>ipvtap</literal> and accepts the + same key as <literal>[IPVLAN]</literal>.</para> + + </refsect1> + + <refsect1> <title>[VXLAN] Section Options</title> <para>The <literal>[VXLAN]</literal> section only applies for netdevs of kind <literal>vxlan</literal>, and accepts the @@ -527,9 +539,9 @@ <variablelist class='network-directives'> <varlistentry> - <term><varname>Id=</varname></term> + <term><varname>VNI=</varname></term> <listitem> - <para>The VXLAN ID to use.</para> + <para>The VXLAN Network Identifier (or VXLAN Segment ID). Takes a number in the range 1-16777215.</para> </listitem> </varlistentry> <varlistentry> @@ -544,6 +556,12 @@ <para>Configures local IP address.</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>Group=</varname></term> + <listitem> + <para>Configures VXLAN multicast group IP address. All members of a VXLAN must use the same multicast group address.</para> + </listitem> + </varlistentry> <varlistentry> <term><varname>TOS=</varname></term> <listitem> @@ -553,10 +571,10 @@ <varlistentry> <term><varname>TTL=</varname></term> <listitem> - <para>A fixed Time To Live N on Virtual eXtensible Local - Area Network packets. N is a number in the range 1–255. 0 - is a special value meaning that packets inherit the TTL - value.</para> + <para>A fixed Time To Live N on Virtual eXtensible Local Area Network packets. + Takes <literal>inherit</literal> or a number in the range 0–255. 0 is a special + value meaning inherit the inner protocol's TTL value. <literal>inherit</literal> + means that it will inherit the outer protocol's TTL value.</para> </listitem> </varlistentry> <varlistentry> @@ -650,6 +668,16 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>GenericProtocolExtension=</varname></term> + <listitem> + <para>Takes a boolean. When true, Generic Protocol Extension extends the existing VXLAN protocol + to provide protocol typing, OAM, and versioning capabilities. For details about the VXLAN GPE + Header, see the <ulink url="https://tools.ietf.org/html/draft-ietf-nvo3-vxlan-gpe-07"> + Generic Protocol Extension for VXLAN </ulink> document. If destination port is not specified and + Generic Protocol Extension is set then default port of 4790 is used. Defaults to false.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>DestinationPort=</varname></term> <listitem> <para>Configures the default destination UDP port on a per-device basis. @@ -675,6 +703,15 @@ The valid range is 0-1048575. </para> </listitem> + </varlistentry> + <varlistentry> + <term><varname>IPDoNotFragment=</varname></term> + <listitem> + <para>Allows to set the IPv4 Do not Fragment (DF) bit in outgoing packets, or to inherit its + value from the IPv4 inner header. Takes a boolean value, or <literal>inherit</literal>. Set + to <literal>inherit</literal> if the encapsulated protocol is IPv6. When unset, the kernel's + default will be used.</para> + </listitem> </varlistentry> </variablelist> </refsect1> @@ -688,7 +725,7 @@ <varlistentry> <term><varname>Id=</varname></term> <listitem> - <para>Specifies the Virtual Network Identifier (VNI) to use. Ranges [0-16777215].</para> + <para>Specifies the Virtual Network Identifier (VNI) to use. Ranges [0-16777215]. This field is mandatory.</para> </listitem> </varlistentry> <varlistentry> @@ -706,7 +743,9 @@ <varlistentry> <term><varname>TTL=</varname></term> <listitem> - <para>Specifies the TTL value to use in outgoing packets. Ranges [1-255].</para> + <para>Accepts the same key in <literal>[VXLAN]</literal> section except when unset or + set to 0, the kernel's default will be used meaning that packets TTL will be set from + <filename>/proc/sys/net/ipv4/ip_default_ttl</filename>.</para> </listitem> </varlistentry> <varlistentry> @@ -739,7 +778,13 @@ <listitem> <para>Specifies the flow label to use in outgoing packets.</para> </listitem> - </varlistentry> + </varlistentry> + <varlistentry> + <term><varname>IPDoNotFragment=</varname></term> + <listitem> + <para>Accepts the same key in <literal>[VXLAN]</literal> section.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> <refsect1> @@ -829,13 +874,13 @@ <varlistentry> <term><varname>Name=</varname></term> <listitem> - <para>Specifies the name of the sesssion. This option is compulsory.</para> + <para>Specifies the name of the session. This option is compulsory.</para> </listitem> </varlistentry> <varlistentry> <term><varname>SessionId=</varname></term> <listitem> - <para>Specifies the sesssion id. The value used must match the <literal>SessionId=</literal> value being used at the peer. + <para>Specifies the session id. The value used must match the <literal>SessionId=</literal> value being used at the peer. Ranges a number between 1 and 4294967295). This option is compulsory.</para> </listitem> </varlistentry> @@ -1265,6 +1310,18 @@ <varname>Encapsulation=GenericUDPEncapsulation</varname>, this must not be specified.</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>Peer=</varname></term> + <listitem> + <para>Configures peer IP address.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>Local=</varname></term> + <listitem> + <para>Configures local IP address.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> <refsect1> @@ -1416,9 +1473,9 @@ </listitem> </varlistentry> <varlistentry> - <term><varname>FwMark=</varname></term> + <term><varname>FirewallMark=</varname></term> <listitem> - <para>Sets a firewall mark on outgoing WireGuard packets from this interface.</para> + <para>Sets a firewall mark on outgoing WireGuard packets from this interface. Takes a number between 1 and 4294967295.</para> </listitem> </varlistentry> </variablelist> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 2d8eeee88f..dd0535a06e 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -77,9 +77,13 @@ is applied, all later files are ignored, even if they match as well.</para> - <para>A network file is said to match a device if each of the - entries in the <literal>[Match]</literal> section matches, or if - the section is empty. The following keys are accepted:</para> + <para>A network file is said to match a network interface if all matches specified by the + <literal>[Match]</literal> section are satisfied. When a network file does not contain valid + settings in <literal>[Match]</literal> section, then the file will match all interfaces and + <command>systemd-networkd</command> warns about that. Hint: to avoid the warning and to make it + clear that all interfaces shall be matched, add the following: + <programlisting>Name=*</programlisting> + The following keys are accepted:</para> <variablelist class='network-directives'> <varlistentry> @@ -322,9 +326,16 @@ <term><varname>LinkLocalAddressing=</varname></term> <listitem> <para>Enables link-local address autoconfiguration. Accepts <literal>yes</literal>, - <literal>no</literal>, <literal>ipv4</literal>, or <literal>ipv6</literal>. If - <varname>Bridge=</varname> is set, defaults to <literal>no</literal>, and if not, - defaults to <literal>ipv6</literal>.</para> + <literal>no</literal>, <literal>ipv4</literal>, <literal>ipv6</literal>, + <literal>fallback</literal>, or <literal>ipv4-fallback</literal>. If + <literal>fallback</literal> or <literal>ipv4-fallback</literal> is specified, then an IPv4 + link-local address is configured only when DHCPv4 fails. If <literal>fallback</literal>, + an IPv6 link-local address is always configured, and if <literal>ipv4-fallback</literal>, + the address is not configured. Note that, the fallback mechanism works only when DHCPv4 + client is enabled, that is, it requires <literal>DHCP=yes</literal> or + <literal>DHCP=ipv4</literal>. If <varname>Bridge=</varname> is set, defaults to + <literal>no</literal>, and if not, defaults to <literal>ipv6</literal>. + </para> </listitem> </varlistentry> <varlistentry> @@ -337,6 +348,15 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>DefaultRouteOnDevice=</varname></term> + <listitem> + <para>Takes a boolean. If set to true, sets up the default route bound to the interface. + Defaults to false. This is useful when creating routes on point-to-point interfaces. + This is equivalent to e.g. the following. + <programlisting>ip route add default dev veth99</programlisting></para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>IPv6Token=</varname></term> <listitem> <para>An IPv6 address with the top 64 bits unset. When set, indicates the @@ -633,7 +653,7 @@ <para>Note that if this option is enabled a userspace implementation of the IPv6 RA protocol is used, and the kernel's own implementation remains disabled, since `networkd` needs to know all details supplied in the advertisements, and these are not available from the kernel if the kernel's - own implemenation is used.</para> + own implementation is used.</para> </listitem> </varlistentry> <varlistentry> @@ -1073,7 +1093,7 @@ <varlistentry> <term><varname>InvertRule=</varname></term> <listitem> - <para>A boolean. Specifies wheather the rule to be inverted. Defaults to false.</para> + <para>A boolean. Specifies whether the rule to be inverted. Defaults to false.</para> </listitem> </varlistentry> </variablelist> @@ -1193,7 +1213,7 @@ <varlistentry> <term><varname>InitialAdvertisedReceiveWindow=</varname></term> <listitem> - <para>The TCP initial advertised receive window is the amount of receive data (in bytes) that can initally be buffered at one time + <para>The TCP initial advertised receive window is the amount of receive data (in bytes) that can initially be buffered at one time on a connection. The sending host can send only that amount of data before waiting for an acknowledgment and window update from the receiving host. Takes a size in bytes between 1 and 4294967295 (2^32 - 1). The usual suffixes K, M, G are supported and are understood to the base of 1024. When unset, the kernel's default will be used. @@ -1208,6 +1228,14 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>FastOpenNoCookie=</varname></term> + <listitem> + <para>Takes a boolean. When true enables TCP fastopen without a cookie on a per-route basis. + When unset, the kernel's default will be used. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>MTUBytes=</varname></term> <listitem> <para>The maximum transmission unit in bytes to set for the @@ -1386,6 +1414,16 @@ </varlistentry> <varlistentry> + <term><varname>MaxAttempts=</varname></term> + <listitem> + <para>Specifies how many times the DHCPv4 client configuration should be attempted. Takes a + number or <literal>infinity</literal>. Defaults to <literal>infinity</literal>. + Note that the time between retries is increased exponentially, so the network will not be + overloaded even if this number is high.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>DUIDType=</varname></term> <listitem> <para>Override the global <varname>DUIDType</varname> setting for this network. See @@ -1450,6 +1488,14 @@ </varlistentry> <varlistentry> + <term><varname>SendRelease=</varname></term> + <listitem> + <para>When true, the DHCPv4 client sends a DHCP release packet when it stops. + Defaults to false.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>RapidCommit=</varname></term> <listitem> <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through @@ -1545,6 +1591,13 @@ </listitem> </varlistentry> + <varlistentry> + <term><varname>BlackList=</varname></term> + <listitem> + <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para> + </listitem> + </varlistentry> + </variablelist> </refsect1> @@ -1873,6 +1926,32 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>ProxyARP=</varname></term> + <listitem> + <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port. + When unset, the kernel's default will be used.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>ProxyARPWiFi=</varname></term> + <listitem> + <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port + which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications. + When unset, the kernel's default will be used.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>MulticastRouter=</varname></term> + <listitem> + <para>Configures this port for having multicast routers attached. A port with a multicast + router will receive all multicast traffic. Takes one of <literal>no</literal> + to disable multicast routers on this port, <literal>query</literal> to let the system detect + the presence of routers, <literal>permanent</literal> to permanently enable multicast traffic + forwarding on this port, or <literal>temporary</literal> to enable multicast routers temporarily + on this port, not depending on incoming queries. When unset, the kernel's default will be used.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>Cost=</varname></term> <listitem> <para>Sets the "cost" of sending packets of this interface. @@ -1910,6 +1989,12 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>Destination=</varname></term> + <listitem> + <para>Takes an IP address of the destination VXLAN tunnel endpoint.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>VLANId=</varname></term> <listitem> <para>The VLAN ID for the new static MAC table entry. If @@ -1917,6 +2002,27 @@ table entry.</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>VNI=</varname></term> + <listitem> + <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to + the remote VXLAN tunnel endpoint. Takes a number in the range 1-16777215. + Defaults to unset.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>AssociatedWith=</varname></term> + <listitem> + <para>Specifies where the address is associated with. Takes one of <literal>use</literal>, + <literal>self</literal>, <literal>master</literal> or <literal>router</literal>. + <literal>use</literal> means the address is in use. User space can use this option to + indicate to the kernel that the fdb entry is in use. <literal>self</literal> means + the address is associated with the port drivers fdb. Usually hardware. <literal>master</literal> + means the address is associated with master devices fdb. <literal>router</literal> means + the destination address is associated with a router. Note that it's valid if the referenced + device is a VXLAN type device and has route shortcircuit enabled. Defaults to <literal>self</literal>.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 1485a26f02..70c6ff33a6 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -429,7 +429,7 @@ <term><varname>Inaccessible=</varname></term> <listitem><para>Masks the specified file or directly in the container, by over-mounting it with an empty file - node of the same type with the most restrictive access mode. Takes a file system path as arugment. This option + node of the same type with the most restrictive access mode. Takes a file system path as argument. This option may be used multiple times to mask multiple files or directories. This option is equivalent to the command line switch <option>--inaccessible=</option>, see <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index e7fb46873c..95209a8a6a 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -245,6 +245,10 @@ <para>This setting is supported only if the unified control group hierarchy is used and disables <varname>MemoryLimit=</varname>.</para> + + <para>Units may have their children use a default <literal>memory.min</literal> value by specifying + <varname>DefaultMemoryMin=</varname>, which has the same semantics as <varname>MemoryMin=</varname>. This setting + does not affect <literal>memory.min</literal> in the unit itself.</para> </listitem> </varlistentry> @@ -266,8 +270,8 @@ <para>This setting is supported only if the unified control group hierarchy is used and disables <varname>MemoryLimit=</varname>.</para> - <para>Units may can have their children use a default <literal>memory.low</literal> value by specifying - <varname>DefaultMemoryLow=</varname>, which has the same usage as <varname>MemoryLow=</varname>. This setting + <para>Units may have their children use a default <literal>memory.low</literal> value by specifying + <varname>DefaultMemoryLow=</varname>, which has the same semantics as <varname>MemoryLow=</varname>. This setting does not affect <literal>memory.low</literal> in the unit itself.</para> </listitem> </varlistentry> diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index 66d63503db..d2a9123b0b 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -148,7 +148,7 @@ <listitem><para>The swap structure will be initialized on the device. If the device is not "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected - that this option remains set even after the device has been initalized.</para> + that this option remains set even after the device has been initialized.</para> <para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored when part of the <varname>Options=</varname> setting in a unit file.</para> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 6a13e52ccf..13b9ed35d2 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -314,7 +314,7 @@ <term><varname>RemainAfterElapse=</varname></term> <listitem><para>Takes a boolean argument. If true, an elapsed - timer will stay loaded, and its state remains queriable. If + timer will stay loaded, and its state remains queryable. If false, an elapsed timer unit that cannot elapse anymore is unloaded. Turning this off is particularly useful for transient timer units that shall disappear after they first diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 81a02253ed..84af5109b8 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -512,7 +512,7 @@ <replaceable>description</replaceable>.</literal>, <literal>Reached target <replaceable>description</replaceable>.</literal>, <literal>Failed to start <replaceable>description</replaceable>.</literal>), so it should be capitalized, and should - not be a full sentence or a phrase with a continous verb. Bad examples include + not be a full sentence or a phrase with a continuous verb. Bad examples include <literal>exiting the container</literal> or <literal>updating the database once per day.</literal>.</para> </listitem> @@ -886,7 +886,7 @@ of powering down the system with similar semantics. <option>exit</option> causes the manager to exit following the normal shutdown procedure, and <option>exit-force</option> causes it terminate without shutting down services. When <option>exit</option> or <option>exit-force</option> is used by default the exit status of the - main process of the unit (if this applies) is returned from the service manager. However, this may be overriden + main process of the unit (if this applies) is returned from the service manager. However, this may be overridden with <varname>FailureActionExitStatus=</varname>/<varname>SuccessActionExitStatus=</varname>, see below.</para></listitem> </varlistentry> @@ -1020,7 +1020,9 @@ or runtime environment doesn't require their functionality. Use the various <varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options for a similar mechanism that causes the job to fail (instead of being skipped) and results in logging about the failed check - (instead of being silently processed). For details about assertion conditions see below.</para> + (instead of being silently processed). For details about assertion conditions see below. Units with failed + conditions are considered to be in a clean state and will be garbage collected if they are not referenced. + This means, that when queried, the condition failure may or may not show up in the state of the unit.</para> <para><varname>ConditionArchitecture=</varname> may be used to check whether the system is running on a specific @@ -1089,6 +1091,7 @@ <literal>lxc-libvirt</literal>, <literal>systemd-nspawn</literal>, <literal>docker</literal>, + <literal>podman</literal>, <literal>rkt</literal>, <literal>wsl</literal>, <literal>acrn</literal> to test @@ -1273,7 +1276,7 @@ <literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>, <literal>></literal>. Compares the number of CPUs in the CPU affinity mask configured of the service manager itself with the specified number, adhering to the specified - comparision operator. On physical systems the number of CPUs in the affinity mask of the service + comparison operator. On physical systems the number of CPUs in the affinity mask of the service manager usually matches the number of physical CPUs, but in special and virtual environments might differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned to the container and not the physically available ones.</para> diff --git a/man/user-system-options.xml b/man/user-system-options.xml index 195c2e4163..8034735658 100644 --- a/man/user-system-options.xml +++ b/man/user-system-options.xml @@ -30,7 +30,7 @@ <para>Execute the operation remotely. Specify a hostname, or a username and hostname separated by <literal>@</literal>, to connect to. The hostname may optionally be suffixed by a - port ssh is listening on, seperated by <literal>:</literal>, and then a + port ssh is listening on, separated by <literal>:</literal>, and then a container name, separated by <literal>/</literal>, which connects directly to a specific container on the specified host. This will use SSH to talk to the remote machine manager diff --git a/man/vtable-example.c b/man/vtable-example.c index a2a6cd18d7..98c20eec52 100644 --- a/man/vtable-example.c +++ b/man/vtable-example.c @@ -59,7 +59,7 @@ int main(int argc, char **argv) { vtable, &object)); - while (true) { + for (;;) { check(sd_bus_wait(bus, UINT64_MAX)); check(sd_bus_process(bus, NULL)); } diff --git a/meson.build b/meson.build index 15e3394b91..eaf0eddcb1 100644 --- a/meson.build +++ b/meson.build @@ -32,10 +32,13 @@ project_source_root = meson.current_source_dir() want_ossfuzz = get_option('oss-fuzz') want_libfuzzer = get_option('llvm-fuzz') -if want_ossfuzz and want_libfuzzer - error('only one of oss-fuzz and llvm-fuzz can be specified') +want_fuzzbuzz = get_option('fuzzbuzz') +if want_ossfuzz + want_libfuzzer + want_fuzzbuzz > 1 + error('only one of oss-fuzz, llvm-fuzz or fuzzbuzz can be specified') endif -fuzzer_build = want_ossfuzz or want_libfuzzer + +skip_deps = want_ossfuzz or want_libfuzzer +fuzzer_build = want_ossfuzz or want_libfuzzer or want_fuzzbuzz ##################################################################### @@ -303,6 +306,8 @@ if want_libfuzzer fuzzing_engine = meson.get_compiler('cpp').find_library('Fuzzer') elif want_ossfuzz fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine') +elif want_fuzzbuzz + fuzzing_engine = meson.get_compiler('cpp').find_library(get_option('fuzzbuzz-engine'), dirs: get_option('fuzzbuzz-engine-dir')) endif possible_cc_flags = [ @@ -411,11 +416,14 @@ endif cpp = ' '.join(cc.cmd_array()) + ' -E' +has_wstringop_truncation = cc.has_argument('-Wstringop-truncation') + ##################################################################### # compilation result tests conf.set('_GNU_SOURCE', true) conf.set('__SANE_USERSPACE_TYPES__', true) +conf.set10('HAVE_WSTRINGOP_TRUNCATION', has_wstringop_truncation) conf.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include <sys/types.h>')) conf.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include <sys/types.h>')) @@ -815,7 +823,7 @@ libmount = dependency('mount', version : fuzzer_build ? '>= 0' : '>= 2.30') want_seccomp = get_option('seccomp') -if want_seccomp != 'false' and not fuzzer_build +if want_seccomp != 'false' and not skip_deps libseccomp = dependency('libseccomp', version : '>= 2.3.1', required : want_seccomp == 'true') @@ -827,7 +835,7 @@ endif conf.set10('HAVE_SECCOMP', have) want_selinux = get_option('selinux') -if want_selinux != 'false' and not fuzzer_build +if want_selinux != 'false' and not skip_deps libselinux = dependency('libselinux', version : '>= 2.1.9', required : want_selinux == 'true') @@ -839,7 +847,7 @@ endif conf.set10('HAVE_SELINUX', have) want_apparmor = get_option('apparmor') -if want_apparmor != 'false' and not fuzzer_build +if want_apparmor != 'false' and not skip_deps libapparmor = dependency('libapparmor', required : want_apparmor == 'true') have = libapparmor.found() @@ -857,7 +865,7 @@ endif want_polkit = get_option('polkit') install_polkit = false install_polkit_pkla = false -if want_polkit != 'false' and not fuzzer_build +if want_polkit != 'false' and not skip_deps install_polkit = true libpolkit = dependency('polkit-gobject-1', @@ -870,7 +878,7 @@ endif conf.set10('ENABLE_POLKIT', install_polkit) want_acl = get_option('acl') -if want_acl != 'false' and not fuzzer_build +if want_acl != 'false' and not skip_deps libacl = cc.find_library('acl', required : want_acl == 'true') have = libacl.found() else @@ -880,7 +888,7 @@ endif conf.set10('HAVE_ACL', have) want_audit = get_option('audit') -if want_audit != 'false' and not fuzzer_build +if want_audit != 'false' and not skip_deps libaudit = dependency('audit', required : want_audit == 'true') have = libaudit.found() else @@ -890,7 +898,7 @@ endif conf.set10('HAVE_AUDIT', have) want_blkid = get_option('blkid') -if want_blkid != 'false' and not fuzzer_build +if want_blkid != 'false' and not skip_deps libblkid = dependency('blkid', required : want_blkid == 'true') have = libblkid.found() else @@ -900,7 +908,7 @@ endif conf.set10('HAVE_BLKID', have) want_kmod = get_option('kmod') -if want_kmod != 'false' and not fuzzer_build +if want_kmod != 'false' and not skip_deps libkmod = dependency('libkmod', version : '>= 15', required : want_kmod == 'true') @@ -912,7 +920,7 @@ endif conf.set10('HAVE_KMOD', have) want_pam = get_option('pam') -if want_pam != 'false' and not fuzzer_build +if want_pam != 'false' and not skip_deps libpam = cc.find_library('pam', required : want_pam == 'true') libpam_misc = cc.find_library('pam_misc', required : want_pam == 'true') have = libpam.found() and libpam_misc.found() @@ -924,7 +932,7 @@ endif conf.set10('HAVE_PAM', have) want_microhttpd = get_option('microhttpd') -if want_microhttpd != 'false' and not fuzzer_build +if want_microhttpd != 'false' and not skip_deps libmicrohttpd = dependency('libmicrohttpd', version : '>= 0.9.33', required : want_microhttpd == 'true') @@ -936,7 +944,7 @@ endif conf.set10('HAVE_MICROHTTPD', have) want_libcryptsetup = get_option('libcryptsetup') -if want_libcryptsetup != 'false' and not fuzzer_build +if want_libcryptsetup != 'false' and not skip_deps libcryptsetup = dependency('libcryptsetup', version : '>= 1.6.0', required : want_libcryptsetup == 'true') @@ -954,7 +962,7 @@ conf.set10('HAVE_LIBCRYPTSETUP', have) conf.set10('HAVE_LIBCRYPTSETUP_SECTOR_SIZE', have_sector) want_libcurl = get_option('libcurl') -if want_libcurl != 'false' and not fuzzer_build +if want_libcurl != 'false' and not skip_deps libcurl = dependency('libcurl', version : '>= 7.32.0', required : want_libcurl == 'true') @@ -971,7 +979,7 @@ if want_libidn == 'true' and want_libidn2 == 'true' error('libidn and libidn2 cannot be requested simultaneously') endif -if want_libidn != 'false' and want_libidn2 != 'true' and not fuzzer_build +if want_libidn != 'false' and want_libidn2 != 'true' and not skip_deps libidn = dependency('libidn', required : want_libidn == 'true') have = libidn.found() @@ -980,7 +988,7 @@ else libidn = [] endif conf.set10('HAVE_LIBIDN', have) -if not have and want_libidn2 != 'false' and not fuzzer_build +if not have and want_libidn2 != 'false' and not skip_deps # libidn is used for both libidn and libidn2 objects libidn = dependency('libidn2', required : want_libidn2 == 'true') @@ -991,7 +999,7 @@ endif conf.set10('HAVE_LIBIDN2', have) want_libiptc = get_option('libiptc') -if want_libiptc != 'false' and not fuzzer_build +if want_libiptc != 'false' and not skip_deps libiptc = dependency('libiptc', required : want_libiptc == 'true') have = libiptc.found() @@ -1002,7 +1010,7 @@ endif conf.set10('HAVE_LIBIPTC', have) want_qrencode = get_option('qrencode') -if want_qrencode != 'false' and not fuzzer_build +if want_qrencode != 'false' and not skip_deps libqrencode = dependency('libqrencode', required : want_qrencode == 'true') have = libqrencode.found() @@ -1013,7 +1021,7 @@ endif conf.set10('HAVE_QRENCODE', have) want_gcrypt = get_option('gcrypt') -if want_gcrypt != 'false' and not fuzzer_build +if want_gcrypt != 'false' and not skip_deps libgcrypt = cc.find_library('gcrypt', required : want_gcrypt == 'true') libgpg_error = cc.find_library('gpg-error', required : want_gcrypt == 'true') have = libgcrypt.found() and libgpg_error.found() @@ -1028,7 +1036,7 @@ endif conf.set10('HAVE_GCRYPT', have) want_gnutls = get_option('gnutls') -if want_gnutls != 'false' and not fuzzer_build +if want_gnutls != 'false' and not skip_deps libgnutls = dependency('gnutls', version : '>= 3.1.4', required : want_gnutls == 'true') @@ -1040,7 +1048,7 @@ endif conf.set10('HAVE_GNUTLS', have) want_openssl = get_option('openssl') -if want_openssl != 'false' and not fuzzer_build +if want_openssl != 'false' and not skip_deps libopenssl = dependency('openssl', version : '>= 1.1.0', required : want_openssl == 'true') @@ -1052,7 +1060,7 @@ endif conf.set10('HAVE_OPENSSL', have) want_elfutils = get_option('elfutils') -if want_elfutils != 'false' and not fuzzer_build +if want_elfutils != 'false' and not skip_deps libdw = dependency('libdw', required : want_elfutils == 'true') have = libdw.found() @@ -1063,7 +1071,7 @@ endif conf.set10('HAVE_ELFUTILS', have) want_zlib = get_option('zlib') -if want_zlib != 'false' and not fuzzer_build +if want_zlib != 'false' and not skip_deps libz = dependency('zlib', required : want_zlib == 'true') have = libz.found() @@ -1074,7 +1082,7 @@ endif conf.set10('HAVE_ZLIB', have) want_bzip2 = get_option('bzip2') -if want_bzip2 != 'false' and not fuzzer_build +if want_bzip2 != 'false' and not skip_deps libbzip2 = cc.find_library('bz2', required : want_bzip2 == 'true') have = libbzip2.found() @@ -1085,7 +1093,7 @@ endif conf.set10('HAVE_BZIP2', have) want_xz = get_option('xz') -if want_xz != 'false' and not fuzzer_build +if want_xz != 'false' and not skip_deps libxz = dependency('liblzma', required : want_xz == 'true') have = libxz.found() @@ -1096,7 +1104,7 @@ endif conf.set10('HAVE_XZ', have) want_lz4 = get_option('lz4') -if want_lz4 != 'false' and not fuzzer_build +if want_lz4 != 'false' and not skip_deps liblz4 = dependency('liblz4', version : '>= 1.3.0', required : want_lz4 == 'true') @@ -1108,7 +1116,7 @@ endif conf.set10('HAVE_LZ4', have) want_xkbcommon = get_option('xkbcommon') -if want_xkbcommon != 'false' and not fuzzer_build +if want_xkbcommon != 'false' and not skip_deps libxkbcommon = dependency('xkbcommon', version : '>= 0.3.0', required : want_xkbcommon == 'true') @@ -1131,7 +1139,7 @@ endif conf.set10('HAVE_PCRE2', have) want_glib = get_option('glib') -if want_glib != 'false' and not fuzzer_build +if want_glib != 'false' and not skip_deps libglib = dependency('glib-2.0', version : '>= 2.22.0', required : want_glib == 'true') @@ -1150,7 +1158,7 @@ endif conf.set10('HAVE_GLIB', have) want_dbus = get_option('dbus') -if want_dbus != 'false' and not fuzzer_build +if want_dbus != 'false' and not skip_deps libdbus = dependency('dbus-1', version : '>= 1.3.2', required : want_dbus == 'true') @@ -1162,7 +1170,7 @@ endif conf.set10('HAVE_DBUS', have) default_dnssec = get_option('default-dnssec') -if fuzzer_build +if skip_deps default_dnssec = 'no' endif if default_dnssec != 'no' and conf.get('HAVE_GCRYPT') == 0 @@ -1203,7 +1211,7 @@ conf.set10('DNS_OVER_TLS_USE_GNUTLS', have_gnutls) conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl) default_dns_over_tls = get_option('default-dns-over-tls') -if fuzzer_build +if skip_deps default_dns_over_tls = 'no' endif if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0 @@ -1234,7 +1242,7 @@ if want_remote != 'false' conf.get('HAVE_LIBCURL') == 1] # sd-j-remote requires µhttpd, and sd-j-upload requires libcurl, so # it's possible to build one without the other. Complain only if - # support was explictly requested. The auxiliary files like sysusers + # support was explicitly requested. The auxiliary files like sysusers # config should be installed when any of the programs are built. if want_remote == 'true' and not (have_deps[0] and have_deps[1]) error('remote support was requested, but dependencies are not available') @@ -2470,6 +2478,7 @@ exe = executable('systemd-mount', 'src/mount/mount-tool.c', include_directories : includes, link_with : [libshared], + dependencies: [libmount], install_rpath : rootlibexecdir, install : true) public_programs += exe @@ -2823,6 +2832,10 @@ foreach tuple : fuzzers sources += 'src/fuzz/fuzz-main.c' endif + if want_fuzzbuzz + sources += 'src/fuzz/fuzzer-entry-point.c' + endif + name = sources[0].split('/')[-1].split('.')[0] fuzzer_exes += executable( diff --git a/meson_options.txt b/meson_options.txt index d4ec37dda2..c1cb4617d0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -329,3 +329,9 @@ option('oss-fuzz', type : 'boolean', value : 'false', description : 'build against oss-fuzz') option('llvm-fuzz', type : 'boolean', value : 'false', description : 'build against LLVM libFuzzer') +option('fuzzbuzz', type : 'boolean', value : 'false', + description : 'build against FuzzBuzz') +option('fuzzbuzz-engine', type : 'string', + description : 'the name of the FuzzBuzz fuzzing engine') +option('fuzzbuzz-engine-dir', type : 'string', + description : 'the directory where the FuzzBuzz fuzzing engine is') diff --git a/network/99-default.link b/network/99-default.link index 92fcbe83ea..347d4b72d2 100644 --- a/network/99-default.link +++ b/network/99-default.link @@ -7,6 +7,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. +[Match] +OriginalName=* + [Link] NamePolicy=keep kernel database onboard slot path MACAddressPolicy=persistent @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2019-01-02 13:27+0100\n" -"PO-Revision-Date: 2019-01-02 13:35+0100\n" +"POT-Creation-Date: 2019-05-05 17:02+0200\n" +"PO-Revision-Date: 2019-05-05 17:13+0200\n" "Last-Translator: Daniele Medri <dmedri@gmail.com>\n" "Language-Team: Italian\n" "Language: it\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.2\n" +"X-Generator: Poedit 2.2.1\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -102,11 +102,11 @@ msgstr "" #: src/hostname/org.freedesktop.hostname1.policy:51 msgid "Get product UUID" -msgstr "Ottieni UUID prodotto" +msgstr "Ottieni UUID del prodotto" #: src/hostname/org.freedesktop.hostname1.policy:52 msgid "Authentication is required to get product UUID." -msgstr "Autenticazione richiesta per ottenere UUID prodotto." +msgstr "Autenticazione richiesta per ottenere UUID del prodotto." #: src/import/org.freedesktop.import1.policy:22 msgid "Import a VM or container image" @@ -168,7 +168,7 @@ msgstr "Consenti alle applicazioni di ritardare lo spegnimento del sistema" #: src/login/org.freedesktop.login1.policy:34 msgid "Authentication is required for an application to delay system shutdown." msgstr "" -"Autenticazione richiesta per un'applicazione per ritardare lo spegnimento " +"Autenticazione richiesta ad un'applicazione per ritardare lo spegnimento " "del sistema." #: src/login/org.freedesktop.login1.policy:44 @@ -178,7 +178,7 @@ msgstr "Consenti alle applicazioni di inibire il sistema in pausa" #: src/login/org.freedesktop.login1.policy:45 msgid "Authentication is required for an application to inhibit system sleep." msgstr "" -"Autenticazione richiesta per un'applicazione per inibire il sistema in pausa." +"Autenticazione richiesta ad un'applicazione per inibire il sistema in pausa." #: src/login/org.freedesktop.login1.policy:55 msgid "Allow applications to delay system sleep" @@ -187,7 +187,7 @@ msgstr "Consenti alle applicazioni di ritardare il sistema in pausa" #: src/login/org.freedesktop.login1.policy:56 msgid "Authentication is required for an application to delay system sleep." msgstr "" -"Autenticazione richiesta per un'applicazione per ritardare il sistema in " +"Autenticazione richiesta ad un'applicazione per ritardare il sistema in " "pausa." #: src/login/org.freedesktop.login1.policy:65 @@ -200,13 +200,13 @@ msgid "" "Authentication is required for an application to inhibit automatic system " "suspend." msgstr "" -"Autenticazione richiesta per un'applicazione per inibire la sospensione " +"Autenticazione richiesta ad un'applicazione per inibire la sospensione " "automatica del sistema." #: src/login/org.freedesktop.login1.policy:75 msgid "Allow applications to inhibit system handling of the power key" msgstr "" -"Consenti alle applicazioni di inibire la gestione di sistema del tasto di " +"Consenti alle applicazioni di inibire la gestione di sistema del tasto" "accensione" #: src/login/org.freedesktop.login1.policy:76 @@ -215,7 +215,7 @@ msgid "" "the power key." msgstr "" "Autenticazione richiesta per un'applicazione per inibire la gestione di " -"sistema del tasto di accensione." +"sistema del tasto accensione." #: src/login/org.freedesktop.login1.policy:86 msgid "Allow applications to inhibit system handling of the suspend key" @@ -228,7 +228,7 @@ msgid "" "Authentication is required for an application to inhibit system handling of " "the suspend key." msgstr "" -"Autenticazione richiesta per un'applicazione per inibire la gestione di " +"Autenticazione richiesta ad un'applicazione per inibire la gestione di " "sistema del tasto di sospensione." #: src/login/org.freedesktop.login1.policy:97 @@ -242,7 +242,7 @@ msgid "" "Authentication is required for an application to inhibit system handling of " "the hibernate key." msgstr "" -"Autenticazione richiesta per un'applicazione per inibire la gestione di " +"Autenticazione richiesta ad un'applicazione per inibire la gestione di " "sistema del tasto di ibernazione." #: src/login/org.freedesktop.login1.policy:107 @@ -479,33 +479,66 @@ msgid "Authentication is required to lock or unlock active sessions." msgstr "Autenticazione richiesta per bloccare o sbloccare le sessioni attive." #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" -msgstr "" -"Permette indicazioni al firmware per avviare l'interfaccia di configurazione" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "Indica il \"motivo\" del riavvio nel kernel" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "" +"Autenticazione richiesta per configurare il \"motivo\" del riavvio nel " +"kernel." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "Indicate al firmware di avviare un'interfaccia di configurazione" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." msgstr "" -"Autenticazione richiesta per indicare al firmware di avviare l'interfaccia " +"Autenticazione richiesta per indicare al firmware l'avvio di un'interfaccia " "di configurazione." -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Indicate al boot loader di avviare un menu" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"Autenticazione richiesta per indicate al boot loader l'avvio di uno " +"specifico menu." + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "Indicare al boot loader di avviare una voce specifica" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"Autenticazione richiesta per indicare al boot loader l'avvio di una " +"specifica voce in elenco." + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "Configura un messaggio per gli utenti" -#: src/login/org.freedesktop.login1.policy:353 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "Autenticazione richiesta per configurare un messaggio per gli utenti" #: src/machine/org.freedesktop.machine1.policy:22 msgid "Log into a local container" -msgstr "Accedi in un container locale" +msgstr "Accedi ad un container locale" #: src/machine/org.freedesktop.machine1.policy:23 msgid "Authentication is required to log into a local container." -msgstr "Autenticazione richiesta per accedere in un container locale." +msgstr "Autenticazione richiesta per accedere ad un container locale." #: src/machine/org.freedesktop.machine1.policy:32 msgid "Log into the local host" @@ -513,7 +546,7 @@ msgstr "Accedi in un host locale" #: src/machine/org.freedesktop.machine1.policy:33 msgid "Authentication is required to log into the local host." -msgstr "Autenticazione richiesta per accedere in un host locale." +msgstr "Autenticazione richiesta per accedere ad un host locale." #: src/machine/org.freedesktop.machine1.policy:42 msgid "Acquire a shell in a local container" @@ -617,7 +650,7 @@ msgstr "Annulla la registrazione di un servizio DNS-SD" #: src/resolve/org.freedesktop.resolve1.policy:34 msgid "Authentication is required to unregister a DNS-SD service" msgstr "" -"Autenticazione richiesta per annullare la registrare di un servizio DNS-SD" +"Autenticazione richiesta per annullare la registrazione di un servizio DNS-SD" #: src/timedate/org.freedesktop.timedate1.policy:22 msgid "Set system time" @@ -659,25 +692,25 @@ msgid "" "shall be enabled." msgstr "" "Autenticazione richiesta per verificare se la sincronizzazione dell'orario " -"in rete possa essere attivata." +"in rete deve essere attivata." -#: src/core/dbus-unit.c:326 +#: src/core/dbus-unit.c:317 msgid "Authentication is required to start '$(unit)'." msgstr "Autenticazione richiesta per avviare '$(unit)'." -#: src/core/dbus-unit.c:327 +#: src/core/dbus-unit.c:318 msgid "Authentication is required to stop '$(unit)'." msgstr "Autenticazione richiesta per fermare '$(unit)'." -#: src/core/dbus-unit.c:328 +#: src/core/dbus-unit.c:319 msgid "Authentication is required to reload '$(unit)'." msgstr "Autenticazione richiesta per ricaricare '$(unit)'." -#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330 +#: src/core/dbus-unit.c:320 src/core/dbus-unit.c:321 msgid "Authentication is required to restart '$(unit)'." msgstr "Autenticazione richiesta per riavviare '$(unit)'." -#: src/core/dbus-unit.c:437 +#: src/core/dbus-unit.c:493 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." @@ -685,11 +718,11 @@ msgstr "" "Autenticazione richiesta per inviare un segnale UNIX ai processi di " "'$(unit)'." -#: src/core/dbus-unit.c:468 +#: src/core/dbus-unit.c:524 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" "Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '$(unit)'." -#: src/core/dbus-unit.c:501 +#: src/core/dbus-unit.c:557 msgid "Authentication is required to set properties on '$(unit)'." msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'." diff --git a/po/pt_BR.po b/po/pt_BR.po index 1f2fb3dd4e..4f37f99bdc 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -2,22 +2,23 @@ # # Brazilian Portuguese translation for systemd. # Enrico Nicoletto <liverig@gmail.com>, 2014. -# Rafael Fontenelle <rafaelff@gnome.org>, 2015, 2017. # Filipe Brandenburger <filbranden@gmail.com>, 2018. +# Rafael Fontenelle <rafaelff@gnome.org>, 2015-2019. +# msgid "" msgstr "" "Project-Id-Version: systemd\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-06 20:10-0700\n" -"PO-Revision-Date: 2018-02-19 10:36-0500\n" -"Last-Translator: Filipe Brandenburger <filbranden@gmail.com>\n" +"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" +"POT-Creation-Date: 2019-05-04 03:28+0000\n" +"PO-Revision-Date: 2019-05-04 11:12-0300\n" +"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n" "Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n" -"Language: pt-BR\n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Zanata 3.9.6\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"X-Generator: Gtranslator 3.32.0\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -47,7 +48,7 @@ msgstr "Gerenciar arquivos de unidades e serviços do sistema" #: src/core/org.freedesktop.systemd1.policy.in:44 msgid "Authentication is required to manage system service or unit files." msgstr "" -"É necessária autenticação para gerenciar arquivos \"unit\" e \"service\" do " +"É necessária autenticação para gerenciar arquivos “unit†e “service†do " "sistema." #: src/core/org.freedesktop.systemd1.policy.in:54 @@ -477,12 +478,20 @@ msgid "Authentication is required to lock or unlock active sessions." msgstr "É necessária autenticação para travar ou destravar sessões ativas." #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" -msgstr "" -"Permitir indicação para o firmware inicializar para a interface de " -"configuração" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "Definir o “motivo†de reinicialização no kernel" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "" +"É necessária autenticação para definir o “motivo†de reinicialização no " +"kernel." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "Indicar para o firmware inicializar para a interface de configuração" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." @@ -490,11 +499,36 @@ msgstr "" "É necessária autenticação para indicar para o firmware inicializar para a " "interface de configuração." -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Indicar para o carregador de inicialização iniciar seu menu" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"É necessária autenticação para indicar para o carregador de inicialização " +"iniciar seu menu" + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "" +"Indicar para o carregador de inicializar iniciar uma entrada especÃfica" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"É necessária autenticação para indicar para o carregador de inicializar " +"iniciar uma entrada especÃfica" + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "Definir uma mensagem de parede" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "É necessária autenticação para definir uma mensagem de parede" @@ -661,31 +695,38 @@ msgstr "" "É necessária autenticação para controlar se deve ser habilitada, ou não, a " "sincronização de horário através de rede." -#: src/core/dbus-unit.c:326 +#: src/core/dbus-unit.c:317 msgid "Authentication is required to start '$(unit)'." -msgstr "É necessária autenticação para iniciar '$(unit)'." +msgstr "É necessária autenticação para iniciar “$(unit)â€." -#: src/core/dbus-unit.c:327 +#: src/core/dbus-unit.c:318 msgid "Authentication is required to stop '$(unit)'." -msgstr "É necessária autenticação para parar '$(unit)'." +msgstr "É necessária autenticação para parar “$(unit)â€." -#: src/core/dbus-unit.c:328 +#: src/core/dbus-unit.c:319 msgid "Authentication is required to reload '$(unit)'." -msgstr "É necessária autenticação para recarregar '$(unit)'." +msgstr "É necessária autenticação para recarregar “$(unit)â€." -#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330 +#: src/core/dbus-unit.c:320 src/core/dbus-unit.c:321 msgid "Authentication is required to restart '$(unit)'." -msgstr "É necessária autenticação para reiniciar '$(unit)'." +msgstr "É necessária autenticação para reiniciar “$(unit)â€." -#: src/core/dbus-unit.c:437 -msgid "Authentication is required to kill '$(unit)'." -msgstr "É necessária autenticação para matar '$(unit)'." +#: src/core/dbus-unit.c:493 +msgid "" +"Authentication is required to send a UNIX signal to the processes of " +"'$(unit)'." +msgstr "" +"É necessária autenticação para enviar um sinal UNIX para os processos de " +"“$(unit)â€." -#: src/core/dbus-unit.c:468 +#: src/core/dbus-unit.c:524 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" -"É necessária autenticação para reiniciar o estado \"failed\" de '$(unit)'." +"É necessária autenticação para reiniciar o estado “failed†de “$(unit)â€." -#: src/core/dbus-unit.c:501 +#: src/core/dbus-unit.c:557 msgid "Authentication is required to set properties on '$(unit)'." -msgstr "É necessária autenticação para definir propriedades em '$(unit)'." +msgstr "É necessária autenticação para definir propriedades em “$(unit)â€." + +#~ msgid "Authentication is required to kill '$(unit)'." +#~ msgstr "É necessária autenticação para matar “$(unit)â€." @@ -2,21 +2,22 @@ # Ukrainian translation for systemd. # Eugene Melnik <jeka7js@gmail.com>, 2014. # Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016, 2018. +# Yuri Chornoivan <yurchor@ukr.net>, 2019. msgid "" msgstr "" "Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2018-03-27 03:26+0000\n" -"PO-Revision-Date: 2018-05-11 23:16+0300\n" -"Last-Translator: Daniel Korostil <ted.korostiled@gmail.com>\n" -"Language-Team: linux.org.ua\n" +"POT-Creation-Date: 2019-03-26 15:29+0000\n" +"PO-Revision-Date: 2019-04-29 19:03+0300\n" +"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" +"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\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" -"X-Generator: Virtaal 0.7.1\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" +"X-Generator: Lokalize 19.03.70\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -25,7 +26,8 @@ msgstr "ÐадіÑлати пароль назад у ÑиÑтему" #: src/core/org.freedesktop.systemd1.policy.in:23 msgid "" "Authentication is required to send the entered passphrase back to the system." -msgstr "Потрібна автентифікаціÑ, щоб надіÑлати введений пароль назад у ÑиÑтему." +msgstr "" +"Потрібна автентифікаціÑ, щоб надіÑлати введений пароль назад у ÑиÑтему." #: src/core/org.freedesktop.systemd1.policy.in:33 msgid "Manage system services or other units" @@ -34,7 +36,8 @@ msgstr "Керувати ÑиÑтемними Ñлужбами й іншими Ð #: src/core/org.freedesktop.systemd1.policy.in:34 msgid "Authentication is required to manage system services or other units." msgstr "" -"Потрібна автентифікаціÑ, щоб керувати ÑиÑтемними Ñлужбами й іншими одиницÑми systemd." +"Потрібна автентифікаціÑ, щоб керувати ÑиÑтемними Ñлужбами й іншими одиницÑми " +"systemd." #: src/core/org.freedesktop.systemd1.policy.in:43 msgid "Manage system service or unit files" @@ -43,14 +46,15 @@ msgstr "Керувати ÑиÑтемними Ñлужбами й Ð¾Ð´Ð¸Ð½Ð¸Ñ†Ñ #: src/core/org.freedesktop.systemd1.policy.in:44 msgid "Authentication is required to manage system service or unit files." msgstr "" -"Потрібна автентифікаціÑ, щоб керувати ÑиÑтемними Ñлужбами й одиницÑми systemd." +"Потрібна автентифікаціÑ, щоб керувати ÑиÑтемними Ñлужбами й одиницÑми " +"systemd." -#: src/core/org.freedesktop.systemd1.policy.in:53 +#: src/core/org.freedesktop.systemd1.policy.in:54 msgid "Set or unset system and service manager environment variables" msgstr "" "Ð’Ñтановити або забрати змінну Ñередовища з ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñлужбами Ñ– ÑиÑтемою" -#: src/core/org.freedesktop.systemd1.policy.in:54 +#: src/core/org.freedesktop.systemd1.policy.in:55 msgid "" "Authentication is required to set or unset system and service manager " "environment variables." @@ -58,27 +62,27 @@ msgstr "" "Потрібна автентифікаціÑ, щоб уÑтановити або забрати змінні Ñередовища з " "ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñлужбами Ñ– ÑиÑтемою." -#: src/core/org.freedesktop.systemd1.policy.in:63 +#: src/core/org.freedesktop.systemd1.policy.in:64 msgid "Reload the systemd state" msgstr "ПерезапуÑтити Ñтан ÑиÑтеми" -#: src/core/org.freedesktop.systemd1.policy.in:64 +#: src/core/org.freedesktop.systemd1.policy.in:65 msgid "Authentication is required to reload the systemd state." msgstr "Потрібна автентифікаціÑ, щоб перезапуÑтити Ñтан ÑиÑтеми." -#: src/hostname/org.freedesktop.hostname1.policy:22 +#: src/hostname/org.freedesktop.hostname1.policy:20 msgid "Set host name" msgstr "Ð’Ñтановити назву вузла" -#: src/hostname/org.freedesktop.hostname1.policy:23 +#: src/hostname/org.freedesktop.hostname1.policy:21 msgid "Authentication is required to set the local host name." msgstr "Потрібна автентифікаціÑ, щоб вÑтановити назву локального вузла." -#: src/hostname/org.freedesktop.hostname1.policy:32 +#: src/hostname/org.freedesktop.hostname1.policy:30 msgid "Set static host name" msgstr "Ð’Ñтановити Ñтатичну назву вузла" -#: src/hostname/org.freedesktop.hostname1.policy:33 +#: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" "Authentication is required to set the statically configured local host name, " "as well as the pretty host name." @@ -86,14 +90,22 @@ msgstr "" "Потрібна автентифікаціÑ, щоб вказати Ñтатично налаштовану назву локального " "вузла, так Ñамо й форматовану." -#: src/hostname/org.freedesktop.hostname1.policy:43 +#: src/hostname/org.freedesktop.hostname1.policy:41 msgid "Set machine information" msgstr "Ð’Ñтановити інформацію про машину" -#: src/hostname/org.freedesktop.hostname1.policy:44 +#: src/hostname/org.freedesktop.hostname1.policy:42 msgid "Authentication is required to set local machine information." msgstr "Потрібна автентифікаціÑ, щоб вказати локальну інформацію про машини." +#: src/hostname/org.freedesktop.hostname1.policy:51 +msgid "Get product UUID" +msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ UUID продукту" + +#: src/hostname/org.freedesktop.hostname1.policy:52 +msgid "Authentication is required to get product UUID." +msgstr "Потрібна автентифікаціÑ, щоб отримати UUID продукту." + #: src/import/org.freedesktop.import1.policy:22 msgid "Import a VM or container image" msgstr "Імпортувати образ контейнера або віртуальної машини" @@ -101,7 +113,8 @@ msgstr "Імпортувати образ контейнера або вірту #: src/import/org.freedesktop.import1.policy:23 msgid "Authentication is required to import a VM or container image" msgstr "" -"Потрібна автентифікаціÑ, щоб імпортувати образ контейнера або віртуальної машини" +"Потрібна автентифікаціÑ, щоб імпортувати образ контейнера або віртуальної " +"машини" #: src/import/org.freedesktop.import1.policy:32 msgid "Export a VM or container image" @@ -120,7 +133,8 @@ msgstr "Звантажити образ контейнера або віртуа #: src/import/org.freedesktop.import1.policy:43 msgid "Authentication is required to download a VM or container image" msgstr "" -"Потрібна автентифікаціÑ, щоб звантажити образ контейнера або віртуальної машини" +"Потрібна автентифікаціÑ, щоб звантажити образ контейнера або віртуальної " +"машини" #: src/locale/org.freedesktop.locale1.policy:22 msgid "Set system locale" @@ -136,7 +150,8 @@ msgstr "Вказати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑиÑтемної ÐºÐ»Ð°Ð²Ñ–Ð°Ñ #: src/locale/org.freedesktop.locale1.policy:34 msgid "Authentication is required to set the system keyboard settings." -msgstr "Потрібна автентифікаціÑ, щоб вказати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑиÑтемної клавіатури." +msgstr "" +"Потрібна автентифікаціÑ, щоб вказати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑиÑтемної клавіатури." #: src/login/org.freedesktop.login1.policy:22 msgid "Allow applications to inhibit system shutdown" @@ -146,7 +161,8 @@ msgstr "Дозволити програмам перешкоджати вимкРmsgid "" "Authentication is required for an application to inhibit system shutdown." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити програмам перешкоджати вимкненню ÑиÑтеми." +"Потрібна автентифікаціÑ, щоб дозволити програмам перешкоджати вимкненню " +"ÑиÑтеми." #: src/login/org.freedesktop.login1.policy:33 msgid "Allow applications to delay system shutdown" @@ -155,7 +171,8 @@ msgstr "Дозволити програмам затримувати вимкнР#: src/login/org.freedesktop.login1.policy:34 msgid "Authentication is required for an application to delay system shutdown." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити програмам затримувати Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ ÑиÑтеми." +"Потрібна автентифікаціÑ, щоб дозволити програмам затримувати Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ " +"ÑиÑтеми." #: src/login/org.freedesktop.login1.policy:44 msgid "Allow applications to inhibit system sleep" @@ -164,7 +181,8 @@ msgstr "Дозволити програмам перешкоджати заÑиР#: src/login/org.freedesktop.login1.policy:45 msgid "Authentication is required for an application to inhibit system sleep." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити програмам перешкоджати заÑинанню ÑиÑтеми." +"Потрібна автентифікаціÑ, щоб дозволити програмам перешкоджати заÑинанню " +"ÑиÑтеми." #: src/login/org.freedesktop.login1.policy:55 msgid "Allow applications to delay system sleep" @@ -173,7 +191,8 @@ msgstr "Дозволити програмами затримувати заÑиР#: src/login/org.freedesktop.login1.policy:56 msgid "Authentication is required for an application to delay system sleep." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити програмам затримувати заÑÐ¸Ð½Ð°Ð½Ð½Ñ ÑиÑтеми." +"Потрібна автентифікаціÑ, щоб дозволити програмам затримувати заÑÐ¸Ð½Ð°Ð½Ð½Ñ " +"ÑиÑтеми." #: src/login/org.freedesktop.login1.policy:65 msgid "Allow applications to inhibit automatic system suspend" @@ -240,16 +259,14 @@ msgstr "" "ÑиÑтемою клавіші Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ ÐºÑ€Ð¸ÑˆÐºÐ¸." #: src/login/org.freedesktop.login1.policy:117 -#| msgid "Allow non-logged-in users to run programs" msgid "Allow non-logged-in user to run programs" msgstr "Дозволити незареєÑтрованим кориÑтувачам запуÑкати програми" #: src/login/org.freedesktop.login1.policy:118 -#| msgid "Authentication is required to run programs as a non-logged-in user." msgid "Explicit request is required to run programs as a non-logged-in user." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити незареєÑтрованим кориÑтувачам запуÑкати " -"програми." +"Потрібна автентифікаціÑ, щоб дозволити незареєÑтрованим кориÑтувачам " +"запуÑкати програми." #: src/login/org.freedesktop.login1.policy:127 msgid "Allow non-logged-in users to run programs" @@ -258,8 +275,8 @@ msgstr "Дозволити незареєÑтрованим кориÑтувач #: src/login/org.freedesktop.login1.policy:128 msgid "Authentication is required to run programs as a non-logged-in user." msgstr "" -"Потрібна автентифікаціÑ, щоб дозволити незареєÑтрованим кориÑтувачам запуÑкати " -"програми." +"Потрібна автентифікаціÑ, щоб дозволити незареєÑтрованим кориÑтувачам " +"запуÑкати програми." #: src/login/org.freedesktop.login1.policy:137 msgid "Allow attaching devices to seats" @@ -276,7 +293,8 @@ msgstr "ОчиÑний приÑтрій Ð´Ð»Ñ Ð¿Ñ–Ð´'єднань до міÑц #: src/login/org.freedesktop.login1.policy:149 msgid "" "Authentication is required for resetting how devices are attached to seats." -msgstr "Потрібна автентифікаціÑ, щоб перезапуÑтити ÑпоÑіб під'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ міÑць." +msgstr "" +"Потрібна автентифікаціÑ, щоб перезапуÑтити ÑпоÑіб під'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ міÑць." #: src/login/org.freedesktop.login1.policy:158 msgid "Power off the system" @@ -326,7 +344,8 @@ msgid "" "Authentication is required for rebooting the system while other users are " "logged in." msgstr "" -"Потрібна автентифікаціÑ, щоб перезапуÑтити ÑиÑтему, коли інші кориÑтувачі в ній." +"Потрібна автентифікаціÑ, щоб перезапуÑтити ÑиÑтему, коли інші кориÑтувачі в " +"ній." #: src/login/org.freedesktop.login1.policy:213 msgid "Reboot the system while an application asked to inhibit it" @@ -337,28 +356,22 @@ msgid "" "Authentication is required for rebooting the system while an application " "asked to inhibit it." msgstr "" -"Потрібна автентифікаціÑ, щоб перезапуÑтити ÑиÑтему, коли програми намагаютьÑÑ " -"перешкодити цьому." +"Потрібна автентифікаціÑ, щоб перезапуÑтити ÑиÑтему, коли програми " +"намагаютьÑÑ Ð¿ÐµÑ€ÐµÑˆÐºÐ¾Ð´Ð¸Ñ‚Ð¸ цьому." #: src/login/org.freedesktop.login1.policy:224 -#| msgid "Hibernate the system" msgid "Halt the system" msgstr "Зупинити ÑиÑтему" #: src/login/org.freedesktop.login1.policy:225 -#| msgid "Authentication is required for hibernating the system." msgid "Authentication is required for halting the system." msgstr "Потрібна автентифікаціÑ, щоб зупинити ÑиÑтему." #: src/login/org.freedesktop.login1.policy:235 -#| msgid "Hibernate the system while other users are logged in" msgid "Halt the system while other users are logged in" msgstr "Зупинити ÑиÑтему, коли інші кориÑтувачі в ній" #: src/login/org.freedesktop.login1.policy:236 -#| msgid "" -#| "Authentication is required for hibernating the system while other users " -#| "are logged in." msgid "" "Authentication is required for halting the system while other users are " "logged in." @@ -366,14 +379,10 @@ msgstr "" "Потрібна автентифікаціÑ, щоб зупинити ÑиÑтему, коли інші кориÑтувачі в ній." #: src/login/org.freedesktop.login1.policy:246 -#| msgid "Hibernate the system while an application asked to inhibit it" msgid "Halt the system while an application asked to inhibit it" msgstr "Зупинити ÑиÑтему, коли програми намагаютьÑÑ Ð¿ÐµÑ€ÐµÑˆÐºÐ¾Ð´Ð¸Ñ‚Ð¸ цьому" #: src/login/org.freedesktop.login1.policy:247 -#| msgid "" -#| "Authentication is required for hibernating the system while an " -#| "application asked to inhibit it." msgid "" "Authentication is required for halting the system while an application asked " "to inhibit it." @@ -398,7 +407,8 @@ msgid "" "Authentication is required for suspending the system while other users are " "logged in." msgstr "" -"Потрібна автентифікаціÑ, щоб призупинити ÑиÑтему, коли інші кориÑтувачі в ній." +"Потрібна автентифікаціÑ, щоб призупинити ÑиÑтему, коли інші кориÑтувачі в " +"ній." #: src/login/org.freedesktop.login1.policy:278 msgid "Suspend the system while an application asked to inhibit it" @@ -463,10 +473,19 @@ msgid "Authentication is required to lock or unlock active sessions." msgstr "Потрібна автентифікаціÑ, щоб заблокувати або розблокувати ÑеанÑи." #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" -msgstr "Дозволити мікрокоду визначати, чи завантажувати Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð²ÑтановленнÑ" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Â«Ð¿Ñ€Ð¸Ñ‡Ð¸Ð½Ð¸Â» Ð¿ÐµÑ€ÐµÐ·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ Ñдрі" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "" +"Потрібна автентифікаціÑ, щоб вÑтановити «причину» Ð¿ÐµÑ€ÐµÐ·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ Ñдрі." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "Ð’Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¼Ñ–ÐºÑ€Ð¾ÐºÐ¾Ð´Ñƒ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñу вÑтановленнÑ" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." @@ -474,11 +493,35 @@ msgstr "" "Потрібна автентифікаціÑ, щоб дозволити мікрокоду визначати, чи завантажувати " "Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð²ÑтановленнÑ." -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Ð’Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÑƒÐ²Ð°Ñ‡Ð° Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð¾ меню завантажувача" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"Потрібна автентифікаціÑ, щоб вказати завантажувачу, що Ñлід завантажитиÑÑ Ð´Ð¾" +" меню завантажувача." + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "Ð’Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÑƒÐ²Ð°Ñ‡Ð° певного пункту Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"Потрібна автентифікаціÑ, щоб вказати завантажувачу, що Ñлід завантажити" +" певний пункт меню завантаженнÑ." + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "Вказати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð° Ñтіні" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "Потрібна автентифікаціÑ, щоб вказати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð° Ñтіні" @@ -504,7 +547,8 @@ msgstr "ПерейнÑти оболонку в локальному контей #: src/machine/org.freedesktop.machine1.policy:43 msgid "Authentication is required to acquire a shell in a local container." -msgstr "Потрібна автентифікаціÑ, щоб перейнÑти оболонку в локальному контейнері." +msgstr "" +"Потрібна автентифікаціÑ, щоб перейнÑти оболонку в локальному контейнері." #: src/machine/org.freedesktop.machine1.policy:53 msgid "Acquire a shell on the local host" @@ -552,15 +596,43 @@ msgid "" "Authentication is required to manage local virtual machine and container " "images." msgstr "" -"Потрібна автентифікаціÑ, щоб керувати локальними образами віртуальних машин Ñ– " -"контейнерів." +"Потрібна автентифікаціÑ, щоб керувати локальними образами віртуальних машин " +"Ñ– контейнерів." + +#: src/portable/org.freedesktop.portable1.policy:13 +msgid "Inspect a portable service image" +msgstr "ІнÑÐ¿ÐµÐºÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ñ€Ð°Ð·Ñƒ портативної Ñлужби" + +#: src/portable/org.freedesktop.portable1.policy:14 +msgid "Authentication is required to inspect a portable service image." +msgstr "Потрібна автентифікаціÑ, щоб інÑпектувати образ портативної Ñлужби." + +#: src/portable/org.freedesktop.portable1.policy:23 +msgid "Attach or detach a portable service image" +msgstr "Ð”Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð¾Ð±Ñ€Ð°Ð·Ñƒ портативної Ñлужби" + +#: src/portable/org.freedesktop.portable1.policy:24 +msgid "" +"Authentication is required to attach or detach a portable service image." +msgstr "" +"Потрібна автентифікаціÑ, щоб долучити або вилучити образ портативної Ñлужби." + +#: src/portable/org.freedesktop.portable1.policy:34 +msgid "Delete or modify portable service image" +msgstr "Ð’Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ внеÑÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½ до образу портативної Ñлужби" + +#: src/portable/org.freedesktop.portable1.policy:35 +msgid "" +"Authentication is required to delete or modify a portable service image." +msgstr "" +"Потрібна автентифікаціÑ, щоб вилучити образ портативної Ñлужби або внеÑти до" +" нього зміни." #: src/resolve/org.freedesktop.resolve1.policy:22 msgid "Register a DNS-SD service" msgstr "ЗареєÑтрувати Ñлужбу DNS-SD" #: src/resolve/org.freedesktop.resolve1.policy:23 -#| msgid "Authentication is required to set a wall message" msgid "Authentication is required to register a DNS-SD service" msgstr "Потрібна автентифікаціÑ, щоб зареєÑтрувати Ñлужбу DNS-SD" @@ -569,7 +641,6 @@ msgid "Unregister a DNS-SD service" msgstr "ЗнÑти з реєÑтрації Ñлужбу DNS-SD" #: src/resolve/org.freedesktop.resolve1.policy:34 -#| msgid "Authentication is required to set a wall message" msgid "Authentication is required to unregister a DNS-SD service" msgstr "Потрібна автентифікаціÑ, щоб знÑти з реєÑтрації Ñлужбу DNS-SD" @@ -597,44 +668,50 @@ msgstr "Вкажіть RTC Ð´Ð»Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ чаÑового поÑÑ msgid "" "Authentication is required to control whether the RTC stores the local or " "UTC time." -msgstr "Потрібна автентифікаціÑ, щоб контролювати, чи зберігає RTC локальний Ñ‡Ð°Ñ Ð°Ð±Ð¾ UTC." +msgstr "" +"Потрібна автентифікаціÑ, щоб контролювати, чи зберігає RTC локальний Ñ‡Ð°Ñ Ð°Ð±Ð¾ " +"UTC." -#: src/timedate/org.freedesktop.timedate1.policy:54 +#: src/timedate/org.freedesktop.timedate1.policy:53 msgid "Turn network time synchronization on or off" msgstr "Увімкнути або вимкнути ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу через мережу" -#: src/timedate/org.freedesktop.timedate1.policy:55 +#: src/timedate/org.freedesktop.timedate1.policy:54 msgid "" "Authentication is required to control whether network time synchronization " "shall be enabled." msgstr "" -"Потрібна автентифікаціÑ, щоб контролювати, чи ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу через мережу " -"запущено." +"Потрібна автентифікаціÑ, щоб контролювати, чи ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу через " +"мережу запущено." -#: src/core/dbus-unit.c:496 +#: src/core/dbus-unit.c:325 msgid "Authentication is required to start '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб запуÑтити «$(unit)»." -#: src/core/dbus-unit.c:497 +#: src/core/dbus-unit.c:326 msgid "Authentication is required to stop '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб зупинити «$(unit)»." -#: src/core/dbus-unit.c:498 +#: src/core/dbus-unit.c:327 msgid "Authentication is required to reload '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб перезавантажити «$(unit)»." -#: src/core/dbus-unit.c:499 src/core/dbus-unit.c:500 +#: src/core/dbus-unit.c:328 src/core/dbus-unit.c:329 msgid "Authentication is required to restart '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб перезапуÑтити «$(unit)»." -#: src/core/dbus-unit.c:607 -msgid "Authentication is required to kill '$(unit)'." -msgstr "Потрібна автентифікаціÑ, щоб вбити «$(unit)»." +#: src/core/dbus-unit.c:434 +msgid "" +"Authentication is required to send a UNIX signal to the processes of " +"'$(unit)'." +msgstr "" +"Потрібна автентифікаціÑ, щоб надіÑлати Ñигнал UNIX до процеÑів «$(unit)»." -#: src/core/dbus-unit.c:638 +#: src/core/dbus-unit.c:465 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб Ñкинути «пошкоджений» Ñтан з «$(unit)»." -#: src/core/dbus-unit.c:671 +#: src/core/dbus-unit.c:498 msgid "Authentication is required to set properties on '$(unit)'." msgstr "Потрібна автентифікаціÑ, щоб вказати влаÑтивоÑÑ‚Ñ– на «$(unit)»." + diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl index 52ed2e3bcb..d4aba59450 100644 --- a/shell-completion/bash/journalctl +++ b/shell-completion/bash/journalctl @@ -83,9 +83,11 @@ _journalctl() { ;; --priority|-p) comps=${__syslog_priorities[*]} + compopt -o nosort ;; --unit|-u) comps=$(journalctl -F '_SYSTEMD_UNIT' 2>/dev/null) + compopt -o filenames ;; --user-unit) comps=$(journalctl -F '_SYSTEMD_USER_UNIT' 2>/dev/null) @@ -100,7 +102,7 @@ _journalctl() { return 0 ;; esac - COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur") ) return 0 fi diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 8c86fed974..8986f4537a 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -114,11 +114,12 @@ __get_all_unit_files () { { __systemctl $1 list-unit-files "$2*"; } | { while re __get_machines() { local a b { machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager; } | \ - { while read a b; do echo " $a"; done; } + { while read a b; do echo " $a"; done; } } _systemctl () { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local cur_orig=$cur local i verb comps mode local -A OPTS=( @@ -221,6 +222,13 @@ _systemctl () { fi done + # When trying to match a unit name with certain special characters in its name (i.e + # foo\x2dbar:01) they get escaped by bash along the way, thus causing any possible + # match to fail. Let's unescape such characters in the verb we're trying to + # autocomplete to avoid this, however, use the original verb (cur_orig) + # during the final match (COMPREPLY) + cur="$(echo $cur | xargs echo)" + if [[ -z $verb ]]; then comps="${VERBS[*]}" @@ -265,7 +273,7 @@ _systemctl () { elif __contains_word "$verb" ${VERBS[ISOLATABLE_UNITS]}; then comps=$( __filter_units_by_properties $mode AllowIsolate=yes \ - $( __get_all_units $mode "$cur" ) ) + $( __get_non_template_units $mode "$cur" ) ) compopt -o filenames elif __contains_word "$verb" ${VERBS[FAILED_UNITS]}; then @@ -306,7 +314,7 @@ _systemctl () { | { while read -r a b; do echo " $a"; done; } ) fi - COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur") ) + COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur_orig") ) return 0 } diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index b4bcba8882..941ea9090b 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -102,8 +102,8 @@ _systemd_nspawn() { comps='no auto guest try-guest host try-host' ;; --bind|--bind-ro) - compopt -o nospace - comps=$(compgen -S/ -A directory -- "$cur" ) + compopt -o nospace -o filenames + comps=$(compgen -f -- "$cur" ) ;; --tmpfs) compopt -o nospace diff --git a/src/activate/activate.c b/src/activate/activate.c index 48aa617e78..8bdf2e50aa 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -474,6 +474,7 @@ int main(int argc, char **argv, char **envp) { int r, n; int epoll_fd = -1; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 97d94516c8..e6bbb179de 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1873,8 +1873,8 @@ static int help(int argc, char *argv[], void *userdata) { " --require Show only requirement in the graph\n" " --from-pattern=GLOB Show only origins in the graph\n" " --to-pattern=GLOB Show only destinations in the graph\n" - " --fuzz=SECONDS Also print also services which finished SECONDS\n" - " earlier than the latest in the branch\n" + " --fuzz=SECONDS Also print services which finished SECONDS earlier\n" + " than the latest in the branch\n" " --man[=BOOL] Do [not] check for existence of man pages\n" " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n" " --iterations=N Show the specified number of iterations\n" @@ -2104,6 +2104,7 @@ static int run(int argc, char *argv[]) { setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index 4637c32819..ca6da40bbc 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -158,6 +158,7 @@ static int run(int argc, char *argv[]) { char **p; int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c index 1e4ee722f2..a16db6824f 100644 --- a/src/basic/alloc-util.c +++ b/src/basic/alloc-util.c @@ -64,8 +64,31 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { if (!q) return NULL; + if (size > 0) { + size_t bn; + + /* Adjust for the 64 byte minimum */ + newalloc = a / size; + + bn = malloc_usable_size(q) / size; + if (bn > newalloc) { + void *qq; + + /* The actual size allocated is larger than what we asked for. Let's call realloc() again to + * take possession of the extra space. This should be cheap, since libc doesn't have to move + * the memory for this. */ + + qq = realloc(q, bn * size); + if (_likely_(qq)) { + *p = qq; + *allocated = bn; + return qq; + } + } + } + *p = q; - *allocated = _unlikely_(size == 0) ? newalloc : malloc_usable_size(q) / size; + *allocated = newalloc; return q; } diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index 5cbaef3eba..6eb224b2c8 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -23,7 +23,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { assert(id); /* We don't convert ENOENT to ESRCH here, since we can't - * really distuingish between "audit is not available in the + * really distinguish between "audit is not available in the * kernel" and "the process does not exist", both which will * result in ENOENT. */ diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c index 3017ecd55d..0d7ea83b75 100644 --- a/src/basic/blockdev-util.c +++ b/src/basic/blockdev-util.c @@ -59,7 +59,7 @@ int get_block_device(const char *path, dev_t *dev) { assert(path); assert(dev); - /* Get's the block device directly backing a file system. If + /* Gets the block device directly backing a file system. If * the block device is encrypted, returns the device mapper * block device. */ diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 210089688b..e6a8b0e0fb 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -1971,7 +1971,7 @@ int cg_slice_to_path(const char *unit, char **ret) { #if HAS_FEATURE_MEMORY_SANITIZER /* msan doesn't instrument stpncpy, so it thinks - * n is later used unitialized: + * n is later used uninitialized: * https://github.com/google/sanitizers/issues/926 */ zero(n); @@ -2616,7 +2616,7 @@ int cg_enable_everywhere( if (r < 0) return r; if (r == 0) { - /* On the legacy hiearchy there's no concept of "enabling" controllers in cgroups defined. Let's claim + /* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim * complete success right away. (If you wonder why we return the full mask here, rather than zero: the * caller tends to use the returned mask later on to compare if all controllers where properly joined, * and if not requeues realization. This use is the primary purpose of the return value, hence let's diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index d010fbb266..7c85022f08 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -99,7 +99,7 @@ static int files_add( /* Does this node have the executable bit set? */ if (flags & CONF_FILES_EXECUTABLE) - /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK) + /* As requested: check if the file is marked executable. Note that we don't check access(X_OK) * here, as we care about whether the file is marked executable at all, and not whether it is * executable for us, because if so, such errors are stuff we should log about. */ diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index b3b840145f..e571f6616e 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -387,7 +387,7 @@ int fd_get_path(int fd, char **ret) { r = readlink_malloc(procfs_path, ret); if (r == -ENOENT) { /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make - * things debuggable and distuingish the two. */ + * things debuggable and distinguish the two. */ if (access("/proc/self/fd/", F_OK) < 0) /* /proc is not available or not set up properly, we're most likely in some chroot diff --git a/src/basic/linux/README b/src/basic/linux/README index f849f34951..2bb70fdaad 100644 --- a/src/basic/linux/README +++ b/src/basic/linux/README @@ -1,3 +1,6 @@ -The files in this directory are copied from kernel-5.0, and the following modifications are applied: +The files in this directory are copied from current kernel master +(b06ed1e7a2fa9b636f368a9e97c3c8877623f8b2) or WireGuard master +(8416093498ac2c754536dad4757c5d86c9ba8809), and the following +modifications are applied: - btrfs.h: drop '__user' attributes - if.h: drop '#include <linux/compiler.h>' and '__user' attributes diff --git a/src/basic/linux/btrfs.h b/src/basic/linux/btrfs.h index 22455c4cd7..b960b45d67 100644 --- a/src/basic/linux/btrfs.h +++ b/src/basic/linux/btrfs.h @@ -837,6 +837,8 @@ enum btrfs_err_code { struct btrfs_ioctl_vol_args) #define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \ struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_FORGET_DEV _IOW(BTRFS_IOCTL_MAGIC, 5, \ + struct btrfs_ioctl_vol_args) /* trans start and trans end are dangerous, and only for * use by applications that know how to avoid the * resulting deadlocks diff --git a/src/basic/linux/btrfs_tree.h b/src/basic/linux/btrfs_tree.h index e974f4bb53..421239b98d 100644 --- a/src/basic/linux/btrfs_tree.h +++ b/src/basic/linux/btrfs_tree.h @@ -307,6 +307,8 @@ * * Used by: * struct btrfs_dir_item.type + * + * Values 0..7 must match common file type values in fs_types.h. */ #define BTRFS_FT_UNKNOWN 0 #define BTRFS_FT_REG_FILE 1 diff --git a/src/basic/linux/fou.h b/src/basic/linux/fou.h index f2ea833a28..87c2c9f088 100644 --- a/src/basic/linux/fou.h +++ b/src/basic/linux/fou.h @@ -16,6 +16,12 @@ enum { FOU_ATTR_IPPROTO, /* u8 */ FOU_ATTR_TYPE, /* u8 */ FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */ + FOU_ATTR_LOCAL_V4, /* u32 */ + FOU_ATTR_LOCAL_V6, /* in6_addr */ + FOU_ATTR_PEER_V4, /* u32 */ + FOU_ATTR_PEER_V6, /* in6_addr */ + FOU_ATTR_PEER_PORT, /* u16 */ + FOU_ATTR_IFINDEX, /* s32 */ __FOU_ATTR_MAX, }; diff --git a/src/basic/linux/if.h b/src/basic/linux/if.h index cbabdde8f9..4c6f030afc 100644 --- a/src/basic/linux/if.h +++ b/src/basic/linux/if.h @@ -179,7 +179,7 @@ enum { }; /* - * Device mapping structure. I'd just gone off and designed a + * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments * for driver options and along come the PCMCIA people 8) * @@ -193,7 +193,7 @@ enum { struct ifmap { unsigned long mem_start; unsigned long mem_end; - unsigned short base_addr; + unsigned short base_addr; unsigned char irq; unsigned char dma; unsigned char port; @@ -233,7 +233,7 @@ struct ifreq { { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ } ifr_ifrn; - + union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; diff --git a/src/basic/linux/if_bonding.h b/src/basic/linux/if_bonding.h index 61a1bf6e86..790585f0e6 100644 --- a/src/basic/linux/if_bonding.h +++ b/src/basic/linux/if_bonding.h @@ -117,6 +117,30 @@ struct ad_info { __u8 partner_system[ETH_ALEN]; }; +/* Embedded inside LINK_XSTATS_TYPE_BOND */ +enum { + BOND_XSTATS_UNSPEC, + BOND_XSTATS_3AD, + __BOND_XSTATS_MAX +}; +#define BOND_XSTATS_MAX (__BOND_XSTATS_MAX - 1) + +/* Embedded inside BOND_XSTATS_3AD */ +enum { + BOND_3AD_STAT_LACPDU_RX, + BOND_3AD_STAT_LACPDU_TX, + BOND_3AD_STAT_LACPDU_UNKNOWN_RX, + BOND_3AD_STAT_LACPDU_ILLEGAL_RX, + BOND_3AD_STAT_MARKER_RX, + BOND_3AD_STAT_MARKER_TX, + BOND_3AD_STAT_MARKER_RESP_RX, + BOND_3AD_STAT_MARKER_RESP_TX, + BOND_3AD_STAT_MARKER_UNKNOWN_RX, + BOND_3AD_STAT_PAD, + __BOND_3AD_STAT_MAX +}; +#define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1) + #endif /* _LINUX_IF_BONDING_H */ /* diff --git a/src/basic/linux/if_ether.h b/src/basic/linux/if_ether.h index 3a45b4ad71..3158ba672b 100644 --- a/src/basic/linux/if_ether.h +++ b/src/basic/linux/if_ether.h @@ -109,6 +109,7 @@ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ diff --git a/src/basic/linux/if_link.h b/src/basic/linux/if_link.h index d653382812..5b225ff63b 100644 --- a/src/basic/linux/if_link.h +++ b/src/basic/linux/if_link.h @@ -925,6 +925,7 @@ enum { enum { LINK_XSTATS_TYPE_UNSPEC, LINK_XSTATS_TYPE_BRIDGE, + LINK_XSTATS_TYPE_BOND, __LINK_XSTATS_TYPE_MAX }; #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) diff --git a/src/basic/linux/if_tun.h b/src/basic/linux/if_tun.h index 23a6753b37..454ae31b93 100644 --- a/src/basic/linux/if_tun.h +++ b/src/basic/linux/if_tun.h @@ -60,6 +60,7 @@ #define TUNSETSTEERINGEBPF _IOR('T', 224, int) #define TUNSETFILTEREBPF _IOR('T', 225, int) #define TUNSETCARRIER _IOW('T', 226, int) +#define TUNGETDEVNETNS _IO('T', 227) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 diff --git a/src/basic/linux/in.h b/src/basic/linux/in.h index a55cb8b101..e7ad9d350a 100644 --- a/src/basic/linux/in.h +++ b/src/basic/linux/in.h @@ -292,10 +292,11 @@ struct sockaddr_in { #define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) /* Defines for Multicast INADDR */ -#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ -#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ -#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ -#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ +#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +#define INADDR_ALLSNOOPERS_GROUP 0xe000006aU /* 224.0.0.106 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ #endif /* <asm/byteorder.h> contains the htonl type stuff.. */ diff --git a/src/basic/linux/in6.h b/src/basic/linux/in6.h index 71d82fe15b..9f2273a083 100644 --- a/src/basic/linux/in6.h +++ b/src/basic/linux/in6.h @@ -178,6 +178,7 @@ struct in6_flowlabel_req { #define IPV6_JOIN_ANYCAST 27 #define IPV6_LEAVE_ANYCAST 28 #define IPV6_MULTICAST_ALL 29 +#define IPV6_ROUTER_ALERT_ISOLATE 30 /* IPV6_MTU_DISCOVER values */ #define IPV6_PMTUDISC_DONT 0 diff --git a/src/basic/linux/update.sh b/src/basic/linux/update.sh new file mode 100755 index 0000000000..b232070193 --- /dev/null +++ b/src/basic/linux/update.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -eu + +for i in *.h */*.h; do + if [[ $i == 'wireguard.h' ]]; then + curl https://raw.githubusercontent.com/WireGuard/WireGuard/master/src/uapi/$i -o $i + else + curl https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/$i -o $i + fi + + sed -i -e 's/__user //g' -e '/^#include <linux\/compiler.h>/ d' $i +done diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index 6f41f50641..ce9daffa7b 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -235,7 +235,7 @@ bool is_locale_utf8(void) { goto out; } - /* For LC_CTYPE=="C" return true, because CTYPE is effectly + /* For LC_CTYPE=="C" return true, because CTYPE is effectively * unset and everything can do to UTF-8 nowadays. */ set = setlocale(LC_CTYPE, NULL); if (!set) { diff --git a/src/basic/log.c b/src/basic/log.c index ea252c4130..a81c350ab4 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -336,7 +336,7 @@ static int write_to_console( char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; struct iovec iovec[6] = {}; - bool highlight; + const char *on = NULL, *off = NULL; size_t n = 0; if (console_fd < 0) @@ -347,18 +347,19 @@ static int write_to_console( iovec[n++] = IOVEC_MAKE_STRING(prefix); } - highlight = LOG_PRI(level) <= LOG_ERR && show_color; + if (show_color) + get_log_colors(LOG_PRI(level), &on, &off, NULL); if (show_location) { (void) snprintf(location, sizeof location, "(%s:%i) ", file, line); iovec[n++] = IOVEC_MAKE_STRING(location); } - if (highlight) - iovec[n++] = IOVEC_MAKE_STRING(ANSI_HIGHLIGHT_RED); + if (on) + iovec[n++] = IOVEC_MAKE_STRING(on); iovec[n++] = IOVEC_MAKE_STRING(buffer); - if (highlight) - iovec[n++] = IOVEC_MAKE_STRING(ANSI_NORMAL); + if (off) + iovec[n++] = IOVEC_MAKE_STRING(off); iovec[n++] = IOVEC_MAKE_STRING("\n"); if (writev(console_fd, iovec, n) < 0) { diff --git a/src/basic/macro.h b/src/basic/macro.h index 1971e912db..ae8907db04 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -105,6 +105,15 @@ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") +#if HAVE_WSTRINGOP_TRUNCATION +# define DISABLE_WARNING_STRINGOP_TRUNCATION \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"") +#else +# define DISABLE_WARNING_STRINGOP_TRUNCATION \ + _Pragma("GCC diagnostic push") +#endif + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 5ac9293167..cb8ddccecd 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -359,6 +359,7 @@ bool fstype_can_uid_gid(const char *fstype) { return STR_IN_SET(fstype, "adfs", + "exfat", "fat", "hfs", "hpfs", diff --git a/src/basic/random-util.c b/src/basic/random-util.c index ca25fd2420..0561f0cb22 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -28,13 +28,15 @@ #include "io-util.h" #include "missing.h" #include "random-util.h" +#include "siphash24.h" #include "time-util.h" int rdrand(unsigned long *ret) { #if defined(__i386__) || defined(__x86_64__) static int have_rdrand = -1; - unsigned char err; + unsigned long v; + uint8_t success; if (have_rdrand < 0) { uint32_t eax, ebx, ecx, edx; @@ -45,7 +47,12 @@ int rdrand(unsigned long *ret) { return -EOPNOTSUPP; } - have_rdrand = !!(ecx & (1U << 30)); +/* Compat with old gcc where bit_RDRND didn't exist yet */ +#ifndef bit_RDRND +#define bit_RDRND (1U << 30) +#endif + + have_rdrand = !!(ecx & bit_RDRND); } if (have_rdrand == 0) @@ -53,12 +60,24 @@ int rdrand(unsigned long *ret) { asm volatile("rdrand %0;" "setc %1" - : "=r" (*ret), - "=qm" (err)); - msan_unpoison(&err, sizeof(err)); - if (!err) + : "=r" (v), + "=qm" (success)); + msan_unpoison(&success, sizeof(success)); + if (!success) return -EAGAIN; + /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success + * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be + * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking + * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around + * only however and something AMD really should fix properly. The Linux kernel should probably work + * around this issue by turning off RDRAND altogether on those CPUs. See: + * https://github.com/systemd/systemd/issues/11810 */ + if (v == 0 || v == ULONG_MAX) + return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), + "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v); + + *ret = v; return 0; #else return -EOPNOTSUPP; @@ -71,21 +90,22 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { bool got_some = false; int r; - /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't - * block, unless the RANDOM_BLOCK flag is set. If RANDOM_DONT_DRAIN is set, an error is returned if the random - * pool is not initialized. Otherwise it will always return some data from the kernel, regardless of whether - * the random pool is fully initialized or not. */ + /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This + * call won't block, unless the RANDOM_BLOCK flag is set. If RANDOM_MAY_FAIL is set, an error is + * returned if the random pool is not initialized. Otherwise it will always return some data from the + * kernel, regardless of whether the random pool is fully initialized or not. */ if (n == 0) return 0; if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND)) - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is not - * required, as we don't trust it (who does?). Note that we only do a single iteration of RDRAND here, - * even though the Intel docs suggest calling this in a tight loop of 10 invocations or so. That's - * because we don't really care about the quality here. We generally prefer using RDRAND if the caller - * allows us too, since this way we won't drain the kernel randomness pool if we don't need it, as the - * pool's entropy is scarce. */ + /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is + * not required, as we don't trust it (who does?). Note that we only do a single iteration of + * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10 + * invocations or so. That's because we don't really care about the quality here. We + * generally prefer using RDRAND if the caller allows us to, since this way we won't upset + * the kernel's random subsystem by accessing it before the pool is initialized (after all it + * will kmsg log about every attempt to do so)..*/ for (;;) { unsigned long u; size_t m; @@ -153,12 +173,13 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { break; } else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to use the syscall the next - * time again though. + /* The kernel has no entropy whatsoever. Let's remember to use the syscall + * the next time again though. * - * If RANDOM_DONT_DRAIN is set, return an error so that random_bytes() can produce some - * pseudo-random bytes instead. Otherwise, fall back to /dev/urandom, which we know is empty, - * but the kernel will produce some bytes for us on a best-effort basis. */ + * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can + * produce some pseudo-random bytes instead. Otherwise, fall back to + * /dev/urandom, which we know is empty, but the kernel will produce some + * bytes for us on a best-effort basis. */ have_syscall = true; if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { @@ -167,7 +188,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { return 0; } - if (FLAGS_SET(flags, RANDOM_DONT_DRAIN)) + if (FLAGS_SET(flags, RANDOM_MAY_FAIL)) return -ENODATA; /* Use /dev/urandom instead */ @@ -196,14 +217,19 @@ void initialize_srand(void) { return; #if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's - * try to make use of that to seed the pseudo-random generator. It's - * better than nothing... */ + /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed + * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder + * to recover the original value by watching any pseudo-random bits we generate. After all the + * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't + * leak the seed for that. */ - auxv = (const void*) getauxval(AT_RANDOM); + auxv = ULONG_TO_PTR(getauxval(AT_RANDOM)); if (auxv) { - assert_cc(sizeof(x) <= 16); - memcpy(&x, auxv, sizeof(x)); + static const uint8_t auxval_hash_key[16] = { + 0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f + }; + + x = (unsigned) siphash24(auxv, 16, auxval_hash_key); } else #endif x = 0; @@ -250,7 +276,7 @@ void pseudo_random_bytes(void *p, size_t n) { void random_bytes(void *p, size_t n) { - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN|RANDOM_ALLOW_RDRAND) >= 0) + if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 3e8c288d3d..148b6c7813 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -8,11 +8,11 @@ typedef enum RandomFlags { RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ - RANDOM_DONT_DRAIN = 1 << 2, /* If we can't get any randomness at all, return early with -EAGAIN */ + RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ } RandomFlags; -int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */ +int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */ void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */ diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h index b8857b0cdf..b12dea2aef 100644 --- a/src/basic/raw-clone.h +++ b/src/basic/raw-clone.h @@ -17,7 +17,7 @@ * @flags: Flags to pass to the clone system call * * Uses the clone system call to create a new process with the cloning flags and termination signal passed in the flags - * parameter. Opposed to glibc's clone funtion, using this function does not set up a separate stack for the child, but + * parameter. Opposed to glibc's clone function, using this function does not set up a separate stack for the child, but * relies on copy-on-write semantics on the one stack at a common virtual address, just as fork does. * * To obtain copy-on-write semantics, flags must not contain CLONE_VM, and thus CLONE_THREAD and CLONE_SIGHAND (which diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index acc0f88cd2..b751933c83 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -33,8 +33,8 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { assert(fd >= 0); - /* This returns the first error we run into, but nevertheless - * tries to go on. This closes the passed fd. */ + /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed + * fd, in all cases, including on failure.. */ if (!(flags & REMOVE_PHYSICAL)) { @@ -85,7 +85,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { is_dir = de->d_type == DT_DIR; if (is_dir) { - int subdir_fd; + _cleanup_close_ int subdir_fd = -1; /* if root_dev is set, remove subdirectories only if device is same */ if (root_dev && st.st_dev != root_dev->st_dev) @@ -104,13 +104,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { if (ret == 0 && r != -ENOENT) ret = r; - safe_close(subdir_fd); continue; } - if (r) { - safe_close(subdir_fd); + if (r > 0) continue; - } if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { @@ -122,24 +119,18 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { if (ret == 0) ret = r; - safe_close(subdir_fd); continue; } - /* ENOTTY, then it wasn't a - * btrfs subvolume, continue - * below. */ - } else { + /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ + } else /* It was a subvolume, continue. */ - safe_close(subdir_fd); continue; - } } - /* We pass REMOVE_PHYSICAL here, to avoid - * doing the fstatfs() to check the file + /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file * system type again for each directory */ - r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); + r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); if (r < 0 && ret == 0) ret = r; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index b23f4c8341..a630856236 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -212,6 +212,12 @@ static inline size_t strlen_ptr(const char *s) { return strlen(s); } +DISABLE_WARNING_STRINGOP_TRUNCATION; +static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) { + strncpy(buf, src, buf_len); +} +REENABLE_WARNING; + /* Like startswith(), but operates on arbitrary memory blocks */ static inline void *memory_startswith(const void *p, size_t sz, const char *token) { size_t n; diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 0b71220ec4..71238ac9c4 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1182,7 +1182,7 @@ bool colors_enabled(void) { * (which is the explicit way to turn colors on/off). If that didn't work we turn colors off unless we are on a * TTY. And if we are on a TTY we turn it off if $TERM is set to "dumb". There's one special tweak though: if * we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open - * continously due to fear of SAK, and hence things are a bit weird. */ + * continuously due to fear of SAK, and hence things are a bit weird. */ if (cached_colors_enabled < 0) { int val; @@ -1334,3 +1334,41 @@ int vt_release(int fd, bool restore) { return 0; } + +void get_log_colors(int priority, const char **on, const char **off, const char **highlight) { + /* Note that this will initialize output variables only when there's something to output. + * The caller must pre-initalize to "" or NULL as appropriate. */ + + if (priority <= LOG_ERR) { + if (on) + *on = ANSI_HIGHLIGHT_RED; + if (off) + *off = ANSI_NORMAL; + if (highlight) + *highlight = ANSI_HIGHLIGHT; + + } else if (priority <= LOG_WARNING) { + if (on) + *on = ANSI_HIGHLIGHT_YELLOW; + if (off) + *off = ANSI_NORMAL; + if (highlight) + *highlight = ANSI_HIGHLIGHT; + + } else if (priority <= LOG_NOTICE) { + if (on) + *on = ANSI_HIGHLIGHT; + if (off) + *off = ANSI_NORMAL; + if (highlight) + *highlight = ANSI_HIGHLIGHT_RED; + + } else if (priority >= LOG_DEBUG) { + if (on) + *on = ANSI_GREY; + if (off) + *off = ANSI_NORMAL; + if (highlight) + *highlight = ANSI_HIGHLIGHT_RED; + } +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index b32528f1d0..2a1851c0f1 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -4,6 +4,7 @@ #include <stdarg.h> #include <stdbool.h> #include <stdio.h> +#include <syslog.h> #include <sys/types.h> #include "macro.h" @@ -159,3 +160,5 @@ int vt_verify_kbmode(int fd); int vt_reset_keyboard(int fd); int vt_restore(int fd); int vt_release(int fd, bool restore_vt); + +void get_log_colors(int priority, const char **on, const char **off, const char **highlight); diff --git a/src/basic/umask-util.h b/src/basic/umask-util.h index e964292eaf..cad745170e 100644 --- a/src/basic/umask-util.h +++ b/src/basic/umask-util.h @@ -8,21 +8,19 @@ #include "macro.h" static inline void umaskp(mode_t *u) { - umask(*u); + umask(*u & 0777); } #define _cleanup_umask_ _cleanup_(umaskp) -struct _umask_struct_ { - mode_t mask; - bool quit; -}; +/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although + * mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to + * distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first + * one, and exit on the second. */ -static inline void _reset_umask_(struct _umask_struct_ *s) { - umask(s->mask); -}; +assert_cc((S_IFMT & 0777) == 0); #define RUN_WITH_UMASK(mask) \ - for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \ - !_saved_umask_.quit ; \ - _saved_umask_.quit = true) + for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \ + FLAGS_SET(_saved_umask_, S_IFMT); \ + _saved_umask_ &= 0777) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index a479590e47..2090675b0d 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -55,7 +55,7 @@ int parse_uid(const char *s, uid_t *ret) { if (!uid_is_valid(uid)) return -ENXIO; /* we return ENXIO instead of EINVAL - * here, to make it easy to distuingish + * here, to make it easy to distinguish * invalid numeric uids from invalid * strings. */ diff --git a/src/basic/virt.c b/src/basic/virt.c index 5dd1bd6633..84b2d64b25 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -234,7 +234,7 @@ static int detect_vm_xen_dom0(void) { if (streq(cap, "control_d")) break; if (!cap) { - log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); + log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)"); return 0; } @@ -428,7 +428,6 @@ finish: } int detect_container(void) { - static const struct { const char *value; int id; @@ -437,6 +436,7 @@ int detect_container(void) { { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, { "docker", VIRTUALIZATION_DOCKER }, + { "podman", VIRTUALIZATION_PODMAN }, { "rkt", VIRTUALIZATION_RKT }, { "wsl", VIRTUALIZATION_WSL }, }; @@ -468,9 +468,15 @@ int detect_container(void) { } if (getpid_cached() == 1) { - /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */ - + /* If we are PID 1 we can just check our own environment variable, and that's authoritative. + * We distinguish three cases: + * - the variable is not defined → we jump to other checks + * - the variable is defined to an empty value → we are not in a container + * - anything else → some container, either one of the known ones or "container-other" + */ e = getenv("container"); + if (!e) + goto check_sched; if (isempty(e)) { r = VIRTUALIZATION_NONE; goto finish; @@ -498,8 +504,9 @@ int detect_container(void) { if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */ log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m"); - /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown - * there is not 1, we know we are in a PID namespace. and hence a container. */ + /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. If the PID + * shown there is not 1, we know we are in a PID namespace and hence a container. */ + check_sched: r = read_one_line_file("/proc/1/sched", &m); if (r >= 0) { const char *t; @@ -649,6 +656,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_LXC] = "lxc", [VIRTUALIZATION_OPENVZ] = "openvz", [VIRTUALIZATION_DOCKER] = "docker", + [VIRTUALIZATION_PODMAN] = "podman", [VIRTUALIZATION_RKT] = "rkt", [VIRTUALIZATION_WSL] = "wsl", [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", diff --git a/src/basic/virt.h b/src/basic/virt.h index c0836897f6..26f409afd0 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -31,6 +31,7 @@ enum { VIRTUALIZATION_LXC, VIRTUALIZATION_OPENVZ, VIRTUALIZATION_DOCKER, + VIRTUALIZATION_PODMAN, VIRTUALIZATION_RKT, VIRTUALIZATION_WSL, VIRTUALIZATION_CONTAINER_OTHER, diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c index 4d72bada62..7f6bad1538 100644 --- a/src/boot/efi/console.c +++ b/src/boot/efi/console.c @@ -114,7 +114,7 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { /* fallback for firmware which does not support SimpleTextInputExProtocol * * This is also called in case ReadKeyStrokeEx did not return a key, because - * some broken firmwares offer SimpleTextInputExProtocol, but never acually + * some broken firmwares offer SimpleTextInputExProtocol, but never actually * handle any key. */ err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k); if (EFI_ERROR(err)) diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c index f6ffed143c..9ec54796ec 100644 --- a/src/boot/efi/shim.c +++ b/src/boot/efi/shim.c @@ -158,7 +158,7 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT if (status != EFI_SUCCESS) return status; - /* No need to check return value, this already happend in efi_main() */ + /* No need to check return value, this already happened in efi_main() */ root = LibOpenRoot(h); dev_path_str = DevicePathToStr(dev_path); diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 86efc02bd8..e702f9366a 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -2508,6 +2508,7 @@ static int busctl_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index 09ee0e86a2..adc028bc8e 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -165,6 +165,7 @@ static void show_cg_info(const char *controller, const char *path) { static int run(int argc, char *argv[]) { int r, output_flags; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index f27c80120d..c548be07ca 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -111,7 +111,7 @@ static bool is_root_cgroup(const char *path) { * * There's one extra complication in all of this, though 😣: if the path to the cgroup indicates we are in the * root cgroup this might actually not be the case, because cgroup namespacing might be in effect - * (CLONE_NEWCGROUP). Since there's no nice way to distuingish a real cgroup root from a fake namespaced one we + * (CLONE_NEWCGROUP). Since there's no nice way to distinguish a real cgroup root from a fake namespaced one we * do an explicit container check here, under the assumption that CLONE_NEWCGROUP is generally used when * container managers are used too. * @@ -909,6 +909,7 @@ static int run(int argc, char *argv[]) { CGroupMask mask; int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index a288c07bc9..6cab4b5ee0 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -237,6 +237,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sStartupIOWeight=%" PRIu64 "\n" "%sBlockIOWeight=%" PRIu64 "\n" "%sStartupBlockIOWeight=%" PRIu64 "\n" + "%sDefaultMemoryMin=%" PRIu64 "\n" "%sDefaultMemoryLow=%" PRIu64 "\n" "%sMemoryMin=%" PRIu64 "\n" "%sMemoryLow=%" PRIu64 "\n" @@ -264,6 +265,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { prefix, c->startup_io_weight, prefix, c->blockio_weight, prefix, c->startup_blockio_weight, + prefix, c->default_memory_min, prefix, c->default_memory_low, prefix, c->memory_min, prefix, c->memory_low, @@ -389,31 +391,34 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) return 0; } -uint64_t unit_get_ancestor_memory_low(Unit *u) { - CGroupContext *c; - - /* 1. Is MemoryLow set in this unit? If so, use that. - * 2. Is DefaultMemoryLow set in any ancestor? If so, use that. - * 3. Otherwise, return CGROUP_LIMIT_MIN. */ - - assert(u); - - c = unit_get_cgroup_context(u); - - if (c->memory_low_set) - return c->memory_low; - - while (UNIT_ISSET(u->slice)) { - u = UNIT_DEREF(u->slice); - c = unit_get_cgroup_context(u); - - if (c->default_memory_low_set) - return c->default_memory_low; - } - - /* We've reached the root, but nobody had DefaultMemoryLow set, so set it to the kernel default. */ - return CGROUP_LIMIT_MIN; -} +#define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \ + uint64_t unit_get_ancestor_##entry(Unit *u) { \ + CGroupContext *c; \ + \ + /* 1. Is entry set in this unit? If so, use that. \ + * 2. Is the default for this entry set in any \ + * ancestor? If so, use that. \ + * 3. Otherwise, return CGROUP_LIMIT_MIN. */ \ + \ + assert(u); \ + \ + c = unit_get_cgroup_context(u); \ + if (c && c->entry##_set) \ + return c->entry; \ + \ + while ((u = UNIT_DEREF(u->slice))) { \ + c = unit_get_cgroup_context(u); \ + if (c && c->default_##entry##_set) \ + return c->default_##entry; \ + } \ + \ + /* We've reached the root, but nobody had default for \ + * this entry set, so set it to the kernel default. */ \ + return CGROUP_LIMIT_MIN; \ +} + +UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low); +UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min); static void cgroup_xattr_apply(Unit *u) { char ids[SD_ID128_STRING_MAX]; @@ -1139,7 +1144,7 @@ static void cgroup_context_apply( } } - /* The bandwith limits are something that make sense to be applied to the host's root but not container + /* The bandwidth limits are something that make sense to be applied to the host's root but not container * roots, as there we want the container manager to handle it */ if (is_host_root || !is_local_root) { if (has_io) { @@ -1305,7 +1310,7 @@ static void cgroup_context_apply( * it also counts. But if the user never set a limit through us (i.e. we are the default of * "unbounded") we leave things unmodified. For this we manage a global boolean that we turn on * the first time we set a limit. Note that this boolean is flushed out on manager reload, - * which is desirable so that there's an offical way to release control of the sysctl from + * which is desirable so that there's an official way to release control of the sysctl from * systemd: set the limit to unbounded and reload. */ if (c->tasks_max != CGROUP_LIMIT_MAX) { @@ -3225,21 +3230,140 @@ int unit_get_ip_accounting( return r; } +static int unit_get_io_accounting_raw(Unit *u, uint64_t ret[static _CGROUP_IO_ACCOUNTING_METRIC_MAX]) { + static const char *const field_names[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "rbytes=", + [CGROUP_IO_WRITE_BYTES] = "wbytes=", + [CGROUP_IO_READ_OPERATIONS] = "rios=", + [CGROUP_IO_WRITE_OPERATIONS] = "wios=", + }; + uint64_t acc[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {}; + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(u); + + if (!u->cgroup_path) + return -ENODATA; + + if (unit_has_host_root_cgroup(u)) + return -ENODATA; /* TODO: return useful data for the top-level cgroup */ + + r = cg_all_unified(); + if (r < 0) + return r; + if (r == 0) /* TODO: support cgroupv1 */ + return -ENODATA; + + if (!FLAGS_SET(u->cgroup_realized_mask, CGROUP_MASK_IO)) + return -ENODATA; + + r = cg_get_path("io", u->cgroup_path, "io.stat", &path); + if (r < 0) + return r; + + f = fopen(path, "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_free_ char *line = NULL; + const char *p; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = line; + p += strcspn(p, WHITESPACE); /* Skip over device major/minor */ + p += strspn(p, WHITESPACE); /* Skip over following whitespace */ + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) { + const char *x; + + x = startswith(word, field_names[i]); + if (x) { + uint64_t w; + + r = safe_atou64(x, &w); + if (r < 0) + return r; + + /* Sum up the stats of all devices */ + acc[i] += w; + break; + } + } + } + } + + memcpy(ret, acc, sizeof(acc)); + return 0; +} + +int unit_get_io_accounting( + Unit *u, + CGroupIOAccountingMetric metric, + bool allow_cache, + uint64_t *ret) { + + uint64_t raw[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; + int r; + + /* Retrieve an IO account parameter. This will subtract the counter when the unit was started. */ + + if (!UNIT_CGROUP_BOOL(u, io_accounting)) + return -ENODATA; + + if (allow_cache && u->io_accounting_last[metric] != UINT64_MAX) + goto done; + + r = unit_get_io_accounting_raw(u, raw); + if (r == -ENODATA && u->io_accounting_last[metric] != UINT64_MAX) + goto done; + if (r < 0) + return r; + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) { + /* Saturated subtraction */ + if (raw[i] > u->io_accounting_base[i]) + u->io_accounting_last[i] = raw[i] - u->io_accounting_base[i]; + else + u->io_accounting_last[i] = 0; + } + +done: + if (ret) + *ret = u->io_accounting_last[metric]; + + return 0; +} + int unit_reset_cpu_accounting(Unit *u) { - nsec_t ns; int r; assert(u); u->cpu_usage_last = NSEC_INFINITY; - r = unit_get_cpu_usage_raw(u, &ns); + r = unit_get_cpu_usage_raw(u, &u->cpu_usage_base); if (r < 0) { u->cpu_usage_base = 0; return r; } - u->cpu_usage_base = ns; return 0; } @@ -3259,6 +3383,35 @@ int unit_reset_ip_accounting(Unit *u) { return r < 0 ? r : q; } +int unit_reset_io_accounting(Unit *u) { + int r; + + assert(u); + + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) + u->io_accounting_last[i] = UINT64_MAX; + + r = unit_get_io_accounting_raw(u, u->io_accounting_base); + if (r < 0) { + zero(u->io_accounting_base); + return r; + } + + return 0; +} + +int unit_reset_accounting(Unit *u) { + int r, q, v; + + assert(u); + + r = unit_reset_cpu_accounting(u); + q = unit_reset_io_accounting(u); + v = unit_reset_ip_accounting(u); + + return r < 0 ? r : q < 0 ? q : v; +} + void unit_invalidate_cgroup(Unit *u, CGroupMask m) { assert(u); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 4bbfa2c3a7..be8be1b7e7 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -25,7 +25,7 @@ typedef enum CGroupDevicePolicy { /* Everything forbidden, except built-in ones and listed ones. */ CGROUP_CLOSED, - /* Everythings forbidden, except for the listed devices */ + /* Everything forbidden, except for the listed devices */ CGROUP_STRICT, _CGROUP_DEVICE_POLICY_MAX, @@ -98,6 +98,7 @@ struct CGroupContext { LIST_HEAD(CGroupIODeviceLimit, io_device_limits); LIST_HEAD(CGroupIODeviceLatency, io_device_latencies); + uint64_t default_memory_min; uint64_t default_memory_low; uint64_t memory_min; uint64_t memory_low; @@ -105,7 +106,9 @@ struct CGroupContext { uint64_t memory_max; uint64_t memory_swap_max; + bool default_memory_min_set; bool default_memory_low_set; + bool memory_min_set; bool memory_low_set; LIST_HEAD(IPAddressAccessItem, ip_address_allow); @@ -139,6 +142,16 @@ typedef enum CGroupIPAccountingMetric { _CGROUP_IP_ACCOUNTING_METRIC_INVALID = -1, } CGroupIPAccountingMetric; +/* Used when querying IO accounting data */ +typedef enum CGroupIOAccountingMetric { + CGROUP_IO_READ_BYTES, + CGROUP_IO_WRITE_BYTES, + CGROUP_IO_READ_OPERATIONS, + CGROUP_IO_WRITE_OPERATIONS, + _CGROUP_IO_ACCOUNTING_METRIC_MAX, + _CGROUP_IO_ACCOUNTING_METRIC_INVALID = -1, +} CGroupIOAccountingMetric; + typedef struct Unit Unit; typedef struct Manager Manager; @@ -196,6 +209,7 @@ Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); +uint64_t unit_get_ancestor_memory_min(Unit *u); uint64_t unit_get_ancestor_memory_low(Unit *u); int unit_search_main_pid(Unit *u, pid_t *ret); @@ -206,10 +220,13 @@ int unit_synthesize_cgroup_empty_event(Unit *u); int unit_get_memory_current(Unit *u, uint64_t *ret); int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); +int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret); int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *ret); int unit_reset_cpu_accounting(Unit *u); int unit_reset_ip_accounting(Unit *u); +int unit_reset_io_accounting(Unit *u); +int unit_reset_accounting(Unit *u); #define UNIT_CGROUP_BOOL(u, name) \ ({ \ diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c index 49d96b7376..fb42865875 100644 --- a/src/core/chown-recursive.c +++ b/src/core/chown-recursive.c @@ -133,17 +133,17 @@ int path_chown_recursive( if (fd < 0) return -errno; - if (!uid_is_valid(uid) && !gid_is_valid(gid)) + if (!uid_is_valid(uid) && !gid_is_valid(gid) && (mask & 07777) == 07777) return 0; /* nothing to do */ if (fstat(fd, &st) < 0) return -errno; - /* Let's take a shortcut: if the top-level directory is properly owned, we don't descend into the whole tree, - * under the assumption that all is OK anyway. */ - + /* Let's take a shortcut: if the top-level directory is properly owned, we don't descend into the + * whole tree, under the assumption that all is OK anyway. */ if ((!uid_is_valid(uid) || st.st_uid == uid) && - (!gid_is_valid(gid) || st.st_gid == gid)) + (!gid_is_valid(gid) || st.st_gid == gid) && + ((st.st_mode & ~mask & 07777) == 0)) return 0; return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid, mask); /* we donate the fd to the call, regardless if it succeeded or failed */ diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 74a583d81b..d75628c663 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -348,6 +348,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), SD_BUS_PROPERTY("DefaultMemoryLow", "t", NULL, offsetof(CGroupContext, default_memory_low), 0), + SD_BUS_PROPERTY("DefaultMemoryMin", "t", NULL, offsetof(CGroupContext, default_memory_min), 0), SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0), SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), @@ -612,6 +613,7 @@ BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_O BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID); BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1); +BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0); BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0); BUS_DEFINE_SET_CGROUP_LIMIT(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale, 1); #pragma GCC diagnostic pop @@ -671,13 +673,16 @@ int bus_cgroup_set_property( return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error); if (streq(name, "MemoryMin")) - return bus_cgroup_set_memory(u, name, &c->memory_min, message, flags, error); + return bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error); if (streq(name, "MemoryLow")) - return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); + return bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error); + + if (streq(name, "DefaultMemoryMin")) + return bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error); if (streq(name, "DefaultMemoryLow")) - return bus_cgroup_set_memory(u, name, &c->default_memory_low, message, flags, error); + return bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error); if (streq(name, "MemoryHigh")) return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); @@ -692,13 +697,16 @@ int bus_cgroup_set_property( return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error); if (streq(name, "MemoryMinScale")) - return bus_cgroup_set_memory_scale(u, name, &c->memory_min, message, flags, error); + return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error); if (streq(name, "MemoryLowScale")) - return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); + return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error); + + if (streq(name, "DefaultMemoryMinScale")) + return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error); if (streq(name, "DefaultMemoryLowScale")) - return bus_cgroup_set_memory_scale(u, name, &c->default_memory_low, message, flags, error); + return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error); if (streq(name, "MemoryHighScale")) return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 83ac7ad110..0aadc98c9b 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -303,8 +303,12 @@ static int bus_socket_set_transient_property( if (streq(name, "SocketProtocol")) return bus_set_transient_socket_protocol(u, name, &s->socket_protocol, message, flags, error); - if ((ci = socket_exec_command_from_string(name)) >= 0) - return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error); + ci = socket_exec_command_from_string(name); + if (ci >= 0) + return bus_set_transient_exec_command(u, name, + &s->exec_command[ci], + message, flags, error); + if (streq(name, "Symlinks")) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 6f1a74d6b5..16b4a6b133 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -19,6 +19,7 @@ #include "selinux-access.h" #include "signal-util.h" #include "special.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -1029,30 +1030,57 @@ static int property_get_ip_counter( void *userdata, sd_bus_error *error) { - CGroupIPAccountingMetric metric; - uint64_t value = (uint64_t) -1; + static const char *const table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IP_INGRESS_BYTES] = "IPIngressBytes", + [CGROUP_IP_EGRESS_BYTES] = "IPEgressBytes", + [CGROUP_IP_INGRESS_PACKETS] = "IPIngressPackets", + [CGROUP_IP_EGRESS_PACKETS] = "IPEgressPackets", + }; + + uint64_t value = UINT64_MAX; Unit *u = userdata; + ssize_t metric; assert(bus); assert(reply); assert(property); assert(u); - if (streq(property, "IPIngressBytes")) - metric = CGROUP_IP_INGRESS_BYTES; - else if (streq(property, "IPIngressPackets")) - metric = CGROUP_IP_INGRESS_PACKETS; - else if (streq(property, "IPEgressBytes")) - metric = CGROUP_IP_EGRESS_BYTES; - else { - assert(streq(property, "IPEgressPackets")); - metric = CGROUP_IP_EGRESS_PACKETS; - } - + assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0); (void) unit_get_ip_accounting(u, metric, &value); return sd_bus_message_append(reply, "t", value); } +static int property_get_io_counter( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + static const char *const table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "IOReadBytes", + [CGROUP_IO_WRITE_BYTES] = "IOWriteBytes", + [CGROUP_IO_READ_OPERATIONS] = "IOReadOperations", + [CGROUP_IO_WRITE_OPERATIONS] = "IOWriteOperations", + }; + + uint64_t value = UINT64_MAX; + Unit *u = userdata; + ssize_t metric; + + assert(bus); + assert(reply); + assert(property); + assert(u); + + assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0); + (void) unit_get_io_accounting(u, metric, false, &value); + return sd_bus_message_append(reply, "t", value); +} + int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; @@ -1176,6 +1204,10 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0), SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0), SD_BUS_PROPERTY("IPEgressPackets", "t", property_get_ip_counter, 0, 0), + SD_BUS_PROPERTY("IOReadBytes", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOReadOperations", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0), + SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0), SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END @@ -1867,7 +1899,7 @@ static int bus_unit_set_transient_property( * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference * dependency type, hence let's not confuse things with that. * - * Note that we don't acually add the reference to the bus track. We do that only after the setup of + * Note that we don't actually add the reference to the bus track. We do that only after the setup of * the transient unit is complete, so that setting this property multiple times in the same transient * unit creation call doesn't count as individual references. */ diff --git a/src/core/dbus.c b/src/core/dbus.c index 8ae5d173f0..a8ce9ac447 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -1257,7 +1257,7 @@ uint64_t manager_bus_n_queued_write(Manager *m) { sd_bus *b; int r; - /* Returns the total number of messages queued for writing on all our direct and API busses. */ + /* Returns the total number of messages queued for writing on all our direct and API buses. */ SET_FOREACH(b, m->private_buses, i) { uint64_t k; diff --git a/src/core/device.c b/src/core/device.c index 9f7caa49ec..f478393f1a 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -420,7 +420,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device * changes while the device unit is already up, let's manually trigger any new units listed in it not - * seen before. This typically appens during the boot-time switch root transition, as udev devices + * seen before. This typically happens during the boot-time switch root transition, as udev devices * will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev * rules only available on the host system, and thus only when the initial udev coldplug trigger runs. * @@ -525,7 +525,7 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool delete = false; - /* Let's remove all dependencies generated due to udev properties. We'll readd whatever is configured + /* Let's remove all dependencies generated due to udev properties. We'll re-add whatever is configured * now below. */ unit_remove_dependencies(u, UNIT_DEPENDENCY_UDEV); } else { @@ -921,7 +921,7 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void * /* A change event can signal that a device is becoming ready, in particular if * the device is using the SYSTEMD_READY logic in udev - * so we need to reach the else block of the follwing if, even for change events */ + * so we need to reach the else block of the following if, even for change events */ if (action == DEVICE_ACTION_REMOVE) { r = swap_process_device_remove(m, dev); if (r < 0) diff --git a/src/core/execute.c b/src/core/execute.c index 5e1a74d0bc..a8b6c92873 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2202,7 +2202,7 @@ static int setup_exec_directory( } /* Lock down the access mode (we use chmod_and_chown() to make this idempotent. We don't - * specifiy UID/GID here, so that path_chown_recursive() can optimize things depending on the + * specify UID/GID here, so that path_chown_recursive() can optimize things depending on the * current UID/GID ownership.) */ r = chmod_and_chown(pp ?: p, context->directories[type].mode, UID_INVALID, GID_INVALID); if (r < 0) @@ -2487,7 +2487,7 @@ static int apply_mount_namespace( bind_mount_free_many(bind_mounts, n_bind_mounts); /* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports - * that with a special, recognizable error ENOANO. In this case, silently proceeed, but only if exclusively + * that with a special, recognizable error ENOANO. In this case, silently proceed, but only if exclusively * sandboxing options were used, i.e. nothing such as RootDirectory= or BindMount= that would result in a * completely different execution environment. */ if (r == -ENOANO) { diff --git a/src/core/job.h b/src/core/job.h index 1b9bcdd895..0f15cbf821 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -72,7 +72,7 @@ enum JobMode { JOB_REPLACE, /* Replace an existing conflicting job */ JOB_REPLACE_IRREVERSIBLY,/* Like JOB_REPLACE + produce irreversible jobs */ JOB_ISOLATE, /* Start a unit, and stop all others */ - JOB_FLUSH, /* Flush out all other queued jobs when queing this one */ + JOB_FLUSH, /* Flush out all other queued jobs when queueing this one */ JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */ JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */ _JOB_MODE_MAX, diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 13e9bbbfb1..b868a367f1 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -172,6 +172,7 @@ $1.CPUQuota, config_parse_cpu_quota, 0, $1.CPUQuotaPeriodSec, config_parse_sec_def_infinity, 0, offsetof($1, cgroup_context.cpu_quota_period_usec) $1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting) $1.MemoryMin, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +$1.DefaultMemoryMin, config_parse_memory_limit, 0, offsetof($1, cgroup_context) $1.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) $1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e7a1f4206b..11a9a7bdeb 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3137,7 +3137,7 @@ int config_parse_memory_limit( bytes = physical_memory_scale(r, 1000U); if (bytes >= UINT64_MAX || - (bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) { + (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) { log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue); return 0; } @@ -3149,9 +3149,16 @@ int config_parse_memory_limit( c->default_memory_low = CGROUP_LIMIT_MIN; else c->default_memory_low = bytes; - } else if (streq(lvalue, "MemoryMin")) + } else if (streq(lvalue, "DefaultMemoryMin")) { + c->default_memory_min_set = true; + if (isempty(rvalue)) + c->default_memory_min = CGROUP_LIMIT_MIN; + else + c->default_memory_min = bytes; + } else if (streq(lvalue, "MemoryMin")) { c->memory_min = bytes; - else if (streq(lvalue, "MemoryLow")) { + c->memory_min_set = true; + } else if (streq(lvalue, "MemoryLow")) { c->memory_low = bytes; c->memory_low_set = true; } else if (streq(lvalue, "MemoryHigh")) diff --git a/src/core/main.c b/src/core/main.c index f80f32183d..e34da11984 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1823,7 +1823,7 @@ static void do_reexecute( args[i++] = NULL; assert(i <= args_size); - /* Reenable any blocked signals, especially important if we switch from initial ramdisk to init=... */ + /* Re-enable any blocked signals, especially important if we switch from initial ramdisk to init=... */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); (void) rlimit_nofile_safe(); @@ -2062,7 +2062,7 @@ static int initialize_runtime( return 0; if (arg_system) { - /* Make sure we leave a core dump without panicing the kernel. */ + /* Make sure we leave a core dump without panicking the kernel. */ install_crash_handler(); if (!skip_setup) { diff --git a/src/core/manager.c b/src/core/manager.c index 66e3403d2c..b7848648be 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -89,7 +89,7 @@ #define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3) #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 -/* If there are more than 1K bus messages queue across our API and direct busses, then let's not add more on top until +/* If there are more than 1K bus messages queue across our API and direct buses, then let's not add more on top until * the queue gets more empty. */ #define MANAGER_BUS_BUSY_THRESHOLD 1024LU @@ -1051,7 +1051,7 @@ static int manager_setup_user_lookup_fd(Manager *m) { * resolutions */ r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-11); if (r < 0) - return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m"); + return log_error_errno(errno, "Failed to set priority of user lookup event source: %m"); (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup"); } @@ -1659,7 +1659,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { r = lookup_paths_reduce(&m->lookup_paths); if (r < 0) - log_warning_errno(r, "Failed ot reduce unit file paths, ignoring: %m"); + log_warning_errno(r, "Failed to reduce unit file paths, ignoring: %m"); manager_build_unit_path_cache(m); @@ -1709,7 +1709,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* Connect to the bus if we are good for it */ manager_setup_bus(m); - /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */ + /* Now that we are connected to all possible buses, let's deserialize who is tracking us. */ r = bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed); if (r < 0) log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m"); @@ -2195,8 +2195,8 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { * overly full before this call we shouldn't increase it in size too wildly in one step, and we * shouldn't monopolize CPU time with generating these messages. Note the difference in counting of * this "budget" and the "threshold" above: the "budget" is decreased only once per generated message, - * regardless how many busses/direct connections it is enqueued on, while the "threshold" is applied to - * each queued instance of bus message, i.e. if the same message is enqueued to five busses/direct + * regardless how many buses/direct connections it is enqueued on, while the "threshold" is applied to + * each queued instance of bus message, i.e. if the same message is enqueued to five buses/direct * connections it will be counted five times. This difference in counting ("references" * vs. "instances") is primarily a result of the fact that it's easier to implement it this way, * however it also reflects the thinking that the "threshold" should put a limit on used queue memory, @@ -2511,7 +2511,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) { if (array) { size_t n = 0; - /* Cound how many entries the array has */ + /* Count how many entries the array has */ while (array[n]) n++; @@ -3548,7 +3548,7 @@ int manager_reload(Manager *m) { r = lookup_paths_reduce(&m->lookup_paths); if (r < 0) - log_warning_errno(r, "Failed ot reduce unit file paths, ignoring: %m"); + log_warning_errno(r, "Failed to reduce unit file paths, ignoring: %m"); manager_build_unit_path_cache(m); diff --git a/src/core/mount.c b/src/core/mount.c index b7fd35fc67..e32db2bf63 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1597,31 +1597,25 @@ static int mount_setup_unit( } static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { - _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; - _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; int r; assert(m); - t = mnt_new_table(); - i = mnt_new_iter(MNT_ITER_FORWARD); - if (!t || !i) - return log_oom(); - - r = mnt_table_parse_mtab(t, NULL); + r = libmount_parse(NULL, NULL, &table, &iter); if (r < 0) return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); for (;;) { struct libmnt_fs *fs; const char *device, *path, *options, *fstype; - int k; - k = mnt_table_next_fs(t, i, &fs); - if (k == 1) + r = mnt_table_next_fs(table, iter, &fs); + if (r == 1) break; - if (k < 0) - return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m"); + if (r < 0) + return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); device = mnt_fs_get_source(fs); path = mnt_fs_get_target(fs); diff --git a/src/core/scope.c b/src/core/scope.c index 7f83052258..bb1e60dd11 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -330,8 +330,7 @@ static int scope_start(Unit *u) { return r; (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); unit_export_state_files(u); diff --git a/src/core/service.c b/src/core/service.c index 56667f03e8..cfb0a7bc72 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -320,10 +320,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { fs->service->n_fd_store--; } - if (fs->event_source) { - sd_event_source_set_enabled(fs->event_source, SD_EVENT_OFF); - sd_event_source_unref(fs->event_source); - } + sd_event_source_disable_unref(fs->event_source); free(fs->fdname); safe_close(fs->fd); @@ -737,7 +734,7 @@ static int service_add_extras(Service *s) { /* If no OOM policy was explicitly set, then default to the configure default OOM policy. Except when * delegation is on, in that case it we assume the payload knows better what to do and can process - * things in a more focussed way. */ + * things in a more focused way. */ if (s->oom_policy < 0) s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->default_oom_policy; @@ -1752,7 +1749,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) * user can still introspect the counter. Do so on the next start. */ s->flush_n_restarts = true; - /* The new state is in effect, let's decrease the fd store ref counter again. Let's also readd us to the GC + /* The new state is in effect, let's decrease the fd store ref counter again. Let's also re-add us to the GC * queue, so that the fd store is possibly gc'ed again */ s->n_keep_fd_store--; unit_add_to_gc_queue(UNIT(s)); @@ -2095,7 +2092,7 @@ static void service_enter_start(Service *s) { /* We force a fake state transition here. Otherwise, the unit would go directly from * SERVICE_DEAD to SERVICE_DEAD without SERVICE_ACTIVATING or SERVICE_ACTIVE - * inbetween. This way we can later trigger actions that depend on the state + * in between. This way we can later trigger actions that depend on the state * transition, including SuccessAction=. */ service_set_state(s, SERVICE_START); @@ -3788,7 +3785,7 @@ static void service_notify_message( r = service_is_suitable_main_pid(s, new_main_pid, LOG_WARNING); if (r == 0) { - /* The new main PID is a bit suspicous, which is OK if the sender is privileged. */ + /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */ if (ucred->uid == 0) { log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid); diff --git a/src/core/slice.c b/src/core/slice.c index 15b18bcad3..489d5ace6a 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -230,8 +230,7 @@ static int slice_start(Unit *u) { return r; (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); slice_set_state(t, SLICE_ACTIVE); return 1; diff --git a/src/core/socket.c b/src/core/socket.c index 836e513d49..b6a4290c3f 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1868,7 +1868,7 @@ static int socket_coldplug(Unit *u) { SOCKET_RUNNING)) { /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors - * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for + * for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN state for * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open, * and if there's a mismatch, warn loudly. */ @@ -2873,7 +2873,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { assert(p); assert(fd >= 0); - /* Similar to socket_address_listen_in_cgroup(), but for accept() rathern than socket(): make sure that any + /* Similar to socket_address_listen_in_cgroup(), but for accept() rather than socket(): make sure that any * connection socket is also properly associated with the cgroup. */ if (!IN_SET(p->address.sockaddr.sa.sa_family, AF_INET, AF_INET6)) diff --git a/src/core/unit.c b/src/core/unit.c index 608a33530c..cab6b44c03 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -113,6 +113,9 @@ Unit *unit_new(Manager *m, size_t size) { RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst); RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); + for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) + u->io_accounting_last[i] = UINT64_MAX; + return u; } @@ -159,7 +162,6 @@ static void unit_init(Unit *u) { cc->cpu_accounting = u->manager->default_cpu_accounting; cc->io_accounting = u->manager->default_io_accounting; - cc->ip_accounting = u->manager->default_ip_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; cc->tasks_accounting = u->manager->default_tasks_accounting; @@ -805,7 +807,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD assert(other); assert(d < _UNIT_DEPENDENCY_MAX); - /* Fix backwards pointers. Let's iterate through all dependendent units of the other unit. */ + /* Fix backwards pointers. Let's iterate through all dependent units of the other unit. */ HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i) { UnitDependency k; @@ -2122,11 +2124,11 @@ void unit_trigger_notify(Unit *u) { } static int unit_log_resources(Unit *u) { - struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + 4]; - bool any_traffic = false, have_ip_accounting = false; - _cleanup_free_ char *igress = NULL, *egress = NULL; + struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4]; + bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false; + _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL; size_t n_message_parts = 0, n_iovec = 0; - char* message_parts[3 + 1], *t; + char* message_parts[1 + 2 + 2 + 1], *t; nsec_t nsec = NSEC_INFINITY; CGroupIPAccountingMetric m; size_t i; @@ -2137,6 +2139,12 @@ static int unit_log_resources(Unit *u) { [CGROUP_IP_EGRESS_BYTES] = "IP_METRIC_EGRESS_BYTES", [CGROUP_IP_EGRESS_PACKETS] = "IP_METRIC_EGRESS_PACKETS", }; + const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "IO_METRIC_READ_BYTES", + [CGROUP_IO_WRITE_BYTES] = "IO_METRIC_WRITE_BYTES", + [CGROUP_IO_READ_OPERATIONS] = "IO_METRIC_READ_OPERATIONS", + [CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS", + }; assert(u); @@ -2166,6 +2174,66 @@ static int unit_log_resources(Unit *u) { message_parts[n_message_parts++] = t; } + for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) { + char buf[FORMAT_BYTES_MAX] = ""; + uint64_t value = UINT64_MAX; + + assert(io_fields[k]); + + (void) unit_get_io_accounting(u, k, k > 0, &value); + if (value == UINT64_MAX) + continue; + + have_io_accounting = true; + if (value > 0) + any_io = true; + + /* Format IO accounting data for inclusion in the structured log message */ + if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) { + r = log_oom(); + goto finish; + } + iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + + /* Format the IO accounting data for inclusion in the human language message string, but only + * for the bytes counters (and not for the operations counters) */ + if (k == CGROUP_IO_READ_BYTES) { + assert(!rr); + rr = strjoin("read ", format_bytes(buf, sizeof(buf), value), " from disk"); + if (!rr) { + r = log_oom(); + goto finish; + } + } else if (k == CGROUP_IO_WRITE_BYTES) { + assert(!wr); + wr = strjoin("written ", format_bytes(buf, sizeof(buf), value), " to disk"); + if (!wr) { + r = log_oom(); + goto finish; + } + } + } + + if (have_io_accounting) { + if (any_io) { + if (rr) + message_parts[n_message_parts++] = TAKE_PTR(rr); + if (wr) + message_parts[n_message_parts++] = TAKE_PTR(wr); + + } else { + char *k; + + k = strdup("no IO"); + if (!k) { + r = log_oom(); + goto finish; + } + + message_parts[n_message_parts++] = k; + } + } + for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) { char buf[FORMAT_BYTES_MAX] = ""; uint64_t value = UINT64_MAX; @@ -3203,6 +3271,20 @@ static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC [CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets", }; +static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base", + [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base", + [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base", + [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base", +}; + +static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last", + [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last", + [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last", + [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last", +}; + int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { CGroupIPAccountingMetric m; int r; @@ -3249,6 +3331,13 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (u->oom_kill_last > 0) (void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last); + for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) { + (void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]); + + if (u->io_accounting_last[im] != UINT64_MAX) + (void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]); + } + if (u->cgroup_path) (void) serialize_item(f, "cgroup", u->cgroup_path); @@ -3324,8 +3413,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { for (;;) { _cleanup_free_ char *line = NULL; - CGroupIPAccountingMetric m; char *l, *v; + ssize_t m; size_t k; r = read_line(f, LONG_LINE_MAX, &line); @@ -3577,10 +3666,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { } /* Check if this is an IP accounting metric serialization field */ - for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) - if (streq(l, ip_accounting_metric_field[m])) - break; - if (m < _CGROUP_IP_ACCOUNTING_METRIC_MAX) { + m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l); + if (m >= 0) { uint64_t c; r = safe_atou64(v, &c); @@ -3591,6 +3678,30 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; } + m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l); + if (m >= 0) { + uint64_t c; + + r = safe_atou64(v, &c); + if (r < 0) + log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v); + else + u->io_accounting_base[m] = c; + continue; + } + + m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l); + if (m >= 0) { + uint64_t c; + + r = safe_atou64(v, &c); + if (r < 0) + log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v); + else + u->io_accounting_last[m] = c; + continue; + } + if (unit_can_serialize(u)) { r = exec_runtime_deserialize_compat(u, l, v, fds); if (r < 0) { @@ -5033,7 +5144,7 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) { p->prefix = u->manager->prefix; SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(u->manager)); - /* Copy paramaters from unit */ + /* Copy parameters from unit */ p->cgroup_path = u->cgroup_path; SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u)); @@ -5394,8 +5505,7 @@ int unit_prepare_exec(Unit *u) { (void) unit_realize_cgroup(u); if (u->reset_accounting) { - (void) unit_reset_cpu_accounting(u); - (void) unit_reset_ip_accounting(u); + (void) unit_reset_accounting(u); u->reset_accounting = false; } diff --git a/src/core/unit.h b/src/core/unit.h index f730f6eee4..007c4aea9e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -51,7 +51,7 @@ static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) { * use this so that we can selectively flush out parts of dependencies again. Note that the same dependency might be * created as a result of multiple "reasons", hence the bitmask. */ typedef enum UnitDependencyMask { - /* Configured directly by the unit file, .wants/.requries symlink or drop-in, or as an immediate result of a + /* Configured directly by the unit file, .wants/.requires symlink or drop-in, or as an immediate result of a * non-dependency option configured that way. */ UNIT_DEPENDENCY_FILE = 1 << 0, @@ -252,11 +252,15 @@ typedef struct Unit { /* The current counter of the oom_kill field in the memory.events cgroup attribute */ uint64_t oom_kill_last; + /* Where the io.stat data was at the time the unit was started */ + uint64_t io_accounting_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; + uint64_t io_accounting_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; /* the most recently read value */ + /* Counterparts in the cgroup filesystem */ char *cgroup_path; CGroupMask cgroup_realized_mask; /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroup v1) */ CGroupMask cgroup_enabled_mask; /* Which controllers are enabled (or more correctly: enabled for the children) for this unit's cgroup? (only relevant on cgroup v2) */ - CGroupMask cgroup_invalidated_mask; /* A mask specifiying controllers which shall be considered invalidated, and require re-realization */ + CGroupMask cgroup_invalidated_mask; /* A mask specifying controllers which shall be considered invalidated, and require re-realization */ CGroupMask cgroup_members_mask; /* A cache for the controllers required by all children of this cgroup (only relevant for slice units) */ /* Inotify watch descriptors for watching cgroup.events and memory.events on cgroupv2 */ diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index ac7b972026..9ab704e207 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -765,7 +765,7 @@ static int submit_coredump( return log_error_errno(r, "Failed to drop privileges: %m"); #if HAVE_ELFUTILS - /* Try to get a strack trace if we can */ + /* Try to get a stack trace if we can */ if (coredump_size <= arg_process_size_max) { _cleanup_free_ char *stacktrace = NULL; @@ -1036,10 +1036,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) * (truncated) copy of what we want to send, and the second one * contains the trailing dots. */ copy[0] = iovec[i]; - copy[1] = (struct iovec) { - .iov_base = (char[]) { '.', '.', '.' }, - .iov_len = 3, - }; + copy[1] = IOVEC_MAKE(((char[]){'.', '.', '.'}), 3); mh.msg_iov = copy; mh.msg_iovlen = 2; diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index b239d81945..6b3f511fa3 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -321,7 +321,7 @@ static int print_field(FILE* file, sd_journal *j) { assert(arg_field); /* A (user-specified) field may appear more than once for a given entry. - * We will print all of the occurences. + * We will print all of the occurrences. * This is different below for fields that systemd-coredump uses, * because they cannot meaningfully appear more than once. */ @@ -790,7 +790,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) /* If neither path or file are specified, we will write to stdout. Let's now check * if stdout is connected to a tty. We checked that the file exists, or that the * core might be stored in the journal. In this second case, if we found the entry, - * in all likelyhood we will be able to access the COREDUMP= field. In either case, + * in all likelihood we will be able to access the COREDUMP= field. In either case, * we stop before doing any "real" work, i.e. before starting decompression or * reading from the file or creating temporary files. */ @@ -1069,6 +1069,7 @@ static int run(int argc, char *argv[]) { int r, units_active; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/delta/delta.c b/src/delta/delta.c index c28a816d67..63baa74794 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -639,6 +639,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r, k, n_found = 0; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c index f05f2279b3..7fb80ca138 100644 --- a/src/detect-virt/detect-virt.c +++ b/src/detect-virt/detect-virt.c @@ -128,6 +128,7 @@ static int run(int argc, char *argv[]) { * to detect whether we are being run in a virtualized * environment or not */ + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/escape/escape.c b/src/escape/escape.c index 8b38d9f679..9066c30853 100644 --- a/src/escape/escape.c +++ b/src/escape/escape.c @@ -159,6 +159,7 @@ static int run(int argc, char *argv[]) { char **i; int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 28ae48d551..76270aec17 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -463,7 +463,7 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - fclose(f); + f = safe_fclose(f); r = generator_open_unit_file(dest, "/etc/fstab", automount_name, &f); if (r < 0) diff --git a/src/fuzz/fuzz-varlink.c b/src/fuzz/fuzz-varlink.c new file mode 100644 index 0000000000..31c13e2bea --- /dev/null +++ b/src/fuzz/fuzz-varlink.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <unistd.h> + +#include "errno-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "hexdecoct.h" +#include "io-util.h" +#include "varlink.h" +#include "log.h" + +static FILE *null = NULL; + +static int method_something(Varlink *v, JsonVariant *p, VarlinkMethodFlags flags, void *userdata) { + json_variant_dump(p, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, null, NULL); + return 0; +} + +static int reply_callback(Varlink *v, JsonVariant *p, const char *error_id, VarlinkReplyFlags flags, void *userdata) { + json_variant_dump(p, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, null, NULL); + return 0; +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + struct iovec *iov = userdata; + bool write_eof = false, read_eof = false; + + assert(s); + assert(fd >= 0); + assert(iov); + + if ((revents & (EPOLLOUT|EPOLLHUP|EPOLLERR)) && iov->iov_len > 0) { + ssize_t n; + + /* never write more than 143 bytes a time, to make broken up recv()s on the other side more + * likely, and thus test some additional code paths. */ + n = send(fd, iov->iov_base, MIN(iov->iov_len, 143U), MSG_NOSIGNAL|MSG_DONTWAIT); + if (n < 0) { + if (ERRNO_IS_DISCONNECT(errno)) + write_eof = true; + else + assert_se(errno == EAGAIN); + } else + IOVEC_INCREMENT(iov, 1, n); + } + + if (revents & EPOLLIN) { + char c[137]; + ssize_t n; + + n = recv(fd, c, sizeof(c), MSG_DONTWAIT); + if (n < 0) { + if (ERRNO_IS_DISCONNECT(errno)) + read_eof = true; + else + assert_se(errno == EAGAIN); + } else if (n == 0) + read_eof = true; + else + hexdump(null, c, (size_t) n); + } + + /* After we wrote everything we could turn off EPOLLOUT. And if we reached read EOF too turn off the + * whole thing. */ + if (write_eof || iov->iov_len == 0) { + + if (read_eof) + assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); + else + assert_se(sd_event_source_set_io_events(s, EPOLLIN) >= 0); + } + + return 0; +} + +static int idle_callback(sd_event_source *s, void *userdata) { + assert(s); + + /* Called as idle callback when there's nothing else to do anymore */ + sd_event_exit(sd_event_source_get_event(s), 0); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct iovec server_iov = IOVEC_MAKE((void*) data, size), client_iov = IOVEC_MAKE((void*) data, size); + /* Important: the declaration order matters here! we want that the fds are closed on return after the + * event sources, hence we declare the fds first, the event sources second */ + _cleanup_close_pair_ int server_pair[2] = { -1, -1 }, client_pair[2] = { -1, -1 }; + _cleanup_(sd_event_source_unrefp) sd_event_source *idle_event_source = NULL, + *server_event_source = NULL, *client_event_source = NULL; + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + + log_set_max_level(LOG_CRIT); + log_parse_environment(); + + assert_se(null = fopen("/dev/null", "we")); + + assert_se(sd_event_default(&e) >= 0); + + /* Test one: write the data as method call to a server */ + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, server_pair) >= 0); + assert_se(varlink_server_new(&s, 0) >= 0); + assert_se(varlink_server_set_description(s, "myserver") >= 0); + assert_se(varlink_server_attach_event(s, e, 0) >= 0); + assert_se(varlink_server_add_connection(s, server_pair[0], NULL) >= 0); + TAKE_FD(server_pair[0]); + assert_se(varlink_server_bind_method(s, "io.test.DoSomething", method_something) >= 0); + assert_se(sd_event_add_io(e, &server_event_source, server_pair[1], EPOLLIN|EPOLLOUT, io_callback, &server_iov) >= 0); + + /* Test two: write the data as method response to a client */ + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, client_pair) >= 0); + assert_se(varlink_connect_fd(&c, client_pair[0]) >= 0); + TAKE_FD(client_pair[0]); + assert_se(varlink_set_description(c, "myclient") >= 0); + assert_se(varlink_attach_event(c, e, 0) >= 0); + assert_se(varlink_bind_reply(c, reply_callback) >= 0); + assert_se(varlink_invoke(c, "io.test.DoSomething", NULL) >= 0); + assert_se(sd_event_add_io(e, &client_event_source, client_pair[1], EPOLLIN|EPOLLOUT, io_callback, &client_iov) >= 0); + + assert_se(sd_event_add_defer(e, &idle_event_source, idle_callback, NULL) >= 0); + assert_se(sd_event_source_set_priority(idle_event_source, SD_EVENT_PRIORITY_IDLE) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + null = safe_fclose(null); + + return 0; +} diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h index 1e56526259..83b1ac11ad 100644 --- a/src/fuzz/fuzz.h +++ b/src/fuzz/fuzz.h @@ -6,3 +6,6 @@ /* The entry point into the fuzzer */ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +/* https://docs.fuzzbuzz.io/developer-documentation/porting-targets-to-fuzzbuzz/libfuzzer-targets */ +int FuzzerEntrypoint(const uint8_t *data, size_t size); diff --git a/src/fuzz/fuzzer-entry-point.c b/src/fuzz/fuzzer-entry-point.c new file mode 100644 index 0000000000..020c111650 --- /dev/null +++ b/src/fuzz/fuzzer-entry-point.c @@ -0,0 +1,5 @@ +#include "fuzz.h" + +int FuzzerEntrypoint(const uint8_t *data, size_t size) { + return LLVMFuzzerTestOneInput(data, size); +} diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build index 0d1ad2b1e1..c88812d1de 100644 --- a/src/fuzz/meson.build +++ b/src/fuzz/meson.build @@ -51,6 +51,10 @@ fuzzers += [ [libshared], []], + [['src/fuzz/fuzz-varlink.c'], + [libshared], + []], + [['src/fuzz/fuzz-unit-file.c'], [libcore, libshared], @@ -128,12 +132,12 @@ fuzzers += [ [['src/fuzz/fuzz-nspawn-settings.c'], [libshared, libnspawn_core], - []], + [libseccomp]], [['src/fuzz/fuzz-nspawn-oci.c'], [libshared, libnspawn_core], - []], + [libseccomp]], [['src/fuzz/fuzz-calendarspec.c'], [libshared], diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 7f9bb49e0c..22de7350fe 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -432,6 +432,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/id128/id128.c b/src/id128/id128.c index 60551ec225..4b370d700d 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -155,6 +155,7 @@ static int id128_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/import/curl-util.c b/src/import/curl-util.c index cd6822f8db..febcc43ce8 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -70,8 +70,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v fd = sd_event_source_get_io_fd(io); assert(fd >= 0); - sd_event_source_set_enabled(io, SD_EVENT_OFF); - sd_event_source_unref(io); + sd_event_source_disable_unref(io); hashmap_remove(g->ios, FD_TO_PTR(s)); hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); @@ -367,7 +366,7 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char if (memchr(p, 0, sz)) return 0; - /* Skip over preceeding whitespace */ + /* Skip over preceding whitespace */ while (sz > 0 && strchr(WHITESPACE, p[0])) { p++; sz--; diff --git a/src/import/importd.c b/src/import/importd.c index 2426933558..f4ca8f4f59 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -250,7 +250,7 @@ static void transfer_send_logs(Transfer *t, bool flush) { n = strndup(t->log_message, e - t->log_message); /* Skip over NUL and newlines */ - while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n')) + while (e < t->log_message + t->log_message_size && IN_SET(*e, 0, '\n')) e++; memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e); diff --git a/src/import/pull-common.c b/src/import/pull-common.c index acfe380969..766b97cb0c 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -436,7 +436,7 @@ int pull_verify(PullJob *main_job, } if (!mkdtemp(gpg_home)) { - r = log_error_errno(errno, "Failed to create tempory home for gpg: %m"); + r = log_error_errno(errno, "Failed to create temporary home for gpg: %m"); goto finish; } diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 04c66a29ce..35efe7002b 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -181,7 +181,7 @@ static int get_source_for_fd(RemoteServer *s, return log_warning_errno(r, "Failed to get writer for source %s: %m", name); - if (s->sources[fd] == NULL) { + if (!s->sources[fd]) { s->sources[fd] = source_new(fd, false, name, writer); if (!s->sources[fd]) { writer_unref(writer); diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index ba51d847e4..d90c6bbd4f 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -33,7 +33,7 @@ /* Both the old and new names are defines, check for the new one. */ -/* Compatiblity with libmicrohttpd < 0.9.38 */ +/* Compatibility with libmicrohttpd < 0.9.38 */ #ifndef MHD_HTTP_NOT_ACCEPTABLE # define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE #endif diff --git a/src/journal/cat.c b/src/journal/cat.c index 9900bd2e86..376b68ad54 100644 --- a/src/journal/cat.c +++ b/src/journal/cat.c @@ -127,6 +127,7 @@ static int run(int argc, char *argv[]) { _cleanup_close_ int outfd = -1, errfd = -1, saved_stderr = -1; int r; + log_show_color(true); log_parse_environment(); log_open(); @@ -146,7 +147,7 @@ static int run(int argc, char *argv[]) { saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); - r = rearrange_stdio(STDIN_FILENO, outfd, errfd < 0 ? outfd : errfd); /* Invalidates fd on succcess + error! */ + r = rearrange_stdio(STDIN_FILENO, outfd, errfd < 0 ? outfd : errfd); /* Invalidates fd on success + error! */ TAKE_FD(outfd); TAKE_FD(errfd); if (r < 0) diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 91d1c2921a..3e285021bd 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -357,8 +357,7 @@ JournalFile* journal_file_close(JournalFile *f) { if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0) journal_file_post_change(f); - (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF); - sd_event_source_unref(f->post_change_timer); + sd_event_source_disable_unref(f->post_change_timer); } journal_file_set_offline(f, true); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 00489098ee..88ee4ee35f 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -52,6 +52,7 @@ #include "logs-show.h" #include "memory-util.h" #include "mkdir.h" +#include "mountpoint-util.h" #include "nulstr-util.h" #include "pager.h" #include "parse-util.h" @@ -67,6 +68,7 @@ #include "tmpfile-util.h" #include "unit-name.h" #include "user-util.h" +#include "varlink.h" #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) @@ -167,6 +169,7 @@ static enum { ACTION_UPDATE_CATALOG, ACTION_LIST_BOOTS, ACTION_FLUSH, + ACTION_RELINQUISH_VAR, ACTION_SYNC, ACTION_ROTATE, ACTION_VACUUM, @@ -368,6 +371,8 @@ static int help(void) { " --vacuum-time=TIME Remove journal files older than specified time\n" " --verify Verify journal file consistency\n" " --sync Synchronize unwritten journal messages to disk\n" + " --relinquish-var Stop logging to disk, log to temporary file system\n" + " --smart-relinquish-var Similar, but NOP if log directory is on root mount\n" " --flush Flush all journal data from /run into /var\n" " --rotate Request immediate rotation of the journal files\n" " --header Show journal header information\n" @@ -415,6 +420,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_UTC, ARG_SYNC, ARG_FLUSH, + ARG_RELINQUISH_VAR, + ARG_SMART_RELINQUISH_VAR, ARG_ROTATE, ARG_VACUUM_SIZE, ARG_VACUUM_FILES, @@ -424,65 +431,67 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version" , no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "pager-end", no_argument, NULL, 'e' }, - { "follow", no_argument, NULL, 'f' }, - { "force", no_argument, NULL, ARG_FORCE }, - { "output", required_argument, NULL, 'o' }, - { "all", no_argument, NULL, 'a' }, - { "full", no_argument, NULL, 'l' }, - { "no-full", no_argument, NULL, ARG_NO_FULL }, - { "lines", optional_argument, NULL, 'n' }, - { "no-tail", no_argument, NULL, ARG_NO_TAIL }, - { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */ - { "quiet", no_argument, NULL, 'q' }, - { "merge", no_argument, NULL, 'm' }, - { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ - { "boot", optional_argument, NULL, 'b' }, - { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, - { "dmesg", no_argument, NULL, 'k' }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "directory", required_argument, NULL, 'D' }, - { "file", required_argument, NULL, ARG_FILE }, - { "root", required_argument, NULL, ARG_ROOT }, - { "header", no_argument, NULL, ARG_HEADER }, - { "identifier", required_argument, NULL, 't' }, - { "priority", required_argument, NULL, 'p' }, - { "grep", required_argument, NULL, 'g' }, - { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE }, - { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, - { "interval", required_argument, NULL, ARG_INTERVAL }, - { "verify", no_argument, NULL, ARG_VERIFY }, - { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, - { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, - { "cursor", required_argument, NULL, 'c' }, - { "cursor-file", required_argument, NULL, ARG_CURSOR_FILE }, - { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, - { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, - { "since", required_argument, NULL, 'S' }, - { "until", required_argument, NULL, 'U' }, - { "unit", required_argument, NULL, 'u' }, - { "user-unit", required_argument, NULL, ARG_USER_UNIT }, - { "field", required_argument, NULL, 'F' }, - { "fields", no_argument, NULL, 'N' }, - { "catalog", no_argument, NULL, 'x' }, - { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, - { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, - { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, - { "reverse", no_argument, NULL, 'r' }, - { "machine", required_argument, NULL, 'M' }, - { "utc", no_argument, NULL, ARG_UTC }, - { "flush", no_argument, NULL, ARG_FLUSH }, - { "sync", no_argument, NULL, ARG_SYNC }, - { "rotate", no_argument, NULL, ARG_ROTATE }, - { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, - { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, - { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, - { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, - { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS }, + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "pager-end", no_argument, NULL, 'e' }, + { "follow", no_argument, NULL, 'f' }, + { "force", no_argument, NULL, ARG_FORCE }, + { "output", required_argument, NULL, 'o' }, + { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, 'l' }, + { "no-full", no_argument, NULL, ARG_NO_FULL }, + { "lines", optional_argument, NULL, 'n' }, + { "no-tail", no_argument, NULL, ARG_NO_TAIL }, + { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */ + { "quiet", no_argument, NULL, 'q' }, + { "merge", no_argument, NULL, 'm' }, + { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ + { "boot", optional_argument, NULL, 'b' }, + { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, + { "dmesg", no_argument, NULL, 'k' }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + { "directory", required_argument, NULL, 'D' }, + { "file", required_argument, NULL, ARG_FILE }, + { "root", required_argument, NULL, ARG_ROOT }, + { "header", no_argument, NULL, ARG_HEADER }, + { "identifier", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "grep", required_argument, NULL, 'g' }, + { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE }, + { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, + { "interval", required_argument, NULL, ARG_INTERVAL }, + { "verify", no_argument, NULL, ARG_VERIFY }, + { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, + { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, + { "cursor", required_argument, NULL, 'c' }, + { "cursor-file", required_argument, NULL, ARG_CURSOR_FILE }, + { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, + { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, + { "since", required_argument, NULL, 'S' }, + { "until", required_argument, NULL, 'U' }, + { "unit", required_argument, NULL, 'u' }, + { "user-unit", required_argument, NULL, ARG_USER_UNIT }, + { "field", required_argument, NULL, 'F' }, + { "fields", no_argument, NULL, 'N' }, + { "catalog", no_argument, NULL, 'x' }, + { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, + { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, + { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, + { "reverse", no_argument, NULL, 'r' }, + { "machine", required_argument, NULL, 'M' }, + { "utc", no_argument, NULL, ARG_UTC }, + { "flush", no_argument, NULL, ARG_FLUSH }, + { "relinquish-var", no_argument, NULL, ARG_RELINQUISH_VAR }, + { "smart-relinquish-var", no_argument, NULL, ARG_SMART_RELINQUISH_VAR }, + { "sync", no_argument, NULL, ARG_SYNC }, + { "rotate", no_argument, NULL, ARG_ROTATE }, + { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, + { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, + { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, + { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, + { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS }, {} }; @@ -914,6 +923,35 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_FLUSH; break; + case ARG_SMART_RELINQUISH_VAR: { + int root_mnt_id, log_mnt_id; + + /* Try to be smart about relinquishing access to /var/log/journal/ during shutdown: + * if it's on the same mount as the root file system there's no point in + * relinquishing access and we can leave journald write to it until the very last + * moment. */ + + r = path_get_mnt_id("/", &root_mnt_id); + if (r < 0) + log_debug_errno(r, "Failed to get root mount ID, ignoring: %m"); + else { + r = path_get_mnt_id("/var/log/journal/", &log_mnt_id); + if (r < 0) + log_debug_errno(r, "Failed to get journal directory mount ID, ignoring: %m"); + else if (root_mnt_id == log_mnt_id) { + log_debug("/var/log/journal/ is on root file system, not relinquishing access to /var."); + return 0; + } else + log_debug("/var/log/journal/ is not on the root file system, relinquishing access to it."); + } + + _fallthrough_; + } + + case ARG_RELINQUISH_VAR: + arg_action = ACTION_RELINQUISH_VAR; + break; + case ARG_ROTATE: arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE; break; @@ -1263,7 +1301,7 @@ static int get_boots( goto finish; } - /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot + /* At this point the read pointer is positioned at the oldest/newest occurrence 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 { @@ -1901,156 +1939,41 @@ static int verify(sd_journal *j) { return r; } -static int watch_run_systemd_journal(uint32_t mask) { - _cleanup_close_ int watch_fd = -1; - - (void) mkdir_p("/run/systemd/journal", 0755); - - watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (watch_fd < 0) - return log_error_errno(errno, "Failed to create inotify object: %m"); - - if (inotify_add_watch(watch_fd, "/run/systemd/journal", mask) < 0) - return log_error_errno(errno, "Failed to watch \"/run/systemd/journal\": %m"); - - return TAKE_FD(watch_fd); -} - -static int flush_to_var(void) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_close_ int watch_fd = -1; +static int simple_varlink_call(const char *option, const char *method) { + _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL; + const char *error; int r; - if (arg_machine) { - log_error("--flush is not supported in conjunction with --machine=."); - return -EOPNOTSUPP; - } - - /* Quick exit */ - if (access("/run/systemd/journal/flushed", F_OK) >= 0) - return 0; + if (arg_machine) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option); - /* OK, let's actually do the full logic, send SIGUSR1 to the - * daemon and set up inotify to wait for the flushed file to appear */ - r = bus_connect_system_systemd(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get D-Bus connection: %m"); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - &error, - NULL, - "ssi", "systemd-journald.service", "main", SIGUSR1); + r = varlink_connect_address(&link, "/run/systemd/journal/io.systemd.journal"); if (r < 0) - return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m"); - watch_fd = watch_run_systemd_journal(IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); - if (watch_fd < 0) - return watch_fd; + (void) varlink_set_description(link, "journal"); - for (;;) { - if (access("/run/systemd/journal/flushed", F_OK) >= 0) - return 0; - - if (errno != ENOENT) - return log_error_errno(errno, "Failed to check for existence of /run/systemd/journal/flushed: %m"); - - r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); - if (r < 0) - return log_error_errno(r, "Failed to wait for event: %m"); + r = varlink_call(link, method, NULL, NULL, &error, NULL); + if (r < 0) + return log_error_errno(r, "Failed to execute varlink call: %s", error); - r = flush_fd(watch_fd); - if (r < 0) - return log_error_errno(r, "Failed to flush inotify events: %m"); - } + return 0; } -static int send_signal_and_wait(int sig, const char *watch_path) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_close_ int watch_fd = -1; - usec_t start; - int r; - - if (arg_machine) { - log_error("--sync and --rotate are not supported in conjunction with --machine=."); - return -EOPNOTSUPP; - } - - start = now(CLOCK_MONOTONIC); - - /* This call sends the specified signal to journald, and waits - * for acknowledgment by watching the mtime of the specified - * flag file. This is used to trigger syncing or rotation and - * then wait for the operation to complete. */ - - for (;;) { - usec_t tstamp; - - /* See if a sync happened by now. */ - r = read_timestamp_file(watch_path, &tstamp); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to read %s: %m", watch_path); - if (r >= 0 && tstamp >= start) - return 0; - - /* Let's ask for a sync, but only once. */ - if (!bus) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - r = bus_connect_system_systemd(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get D-Bus connection: %m"); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - &error, - NULL, - "ssi", "systemd-journald.service", "main", sig); - if (r < 0) - return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); - - continue; - } - - /* Let's install the inotify watch, if we didn't do that yet. */ - if (watch_fd < 0) { - watch_fd = watch_run_systemd_journal(IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR); - if (watch_fd < 0) - return watch_fd; - - /* Recheck the flag file immediately, so that we don't miss any event since the last check. */ - continue; - } - - /* OK, all preparatory steps done, let's wait until inotify reports an event. */ - - r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); - if (r < 0) - return log_error_errno(r, "Failed to wait for event: %m"); - - r = flush_fd(watch_fd); - if (r < 0) - return log_error_errno(r, "Failed to flush inotify events: %m"); - } +static int flush_to_var(void) { + return simple_varlink_call("--flush", "io.systemd.Journal.FlushToVar"); +} - return 0; +static int relinquish_var(void) { + return simple_varlink_call("--relinquish-var", "io.systemd.Journal.RelinquishVar"); } static int rotate(void) { - return send_signal_and_wait(SIGUSR2, "/run/systemd/journal/rotated"); + return simple_varlink_call("--rotate", "io.systemd.Journal.Rotate"); } static int sync_journal(void) { - return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced"); + return simple_varlink_call("--sync", "io.systemd.Journal.Synchronize"); } static int wait_for_change(sd_journal *j, int poll_fd) { @@ -2100,6 +2023,7 @@ int main(int argc, char *argv[]) { int n_shown = 0, r, poll_fd = -1; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); @@ -2159,6 +2083,10 @@ int main(int argc, char *argv[]) { r = flush_to_var(); goto finish; + case ACTION_RELINQUISH_VAR: + r = relinquish_var(); + goto finish; + case ACTION_SYNC: r = sync_journal(); goto finish; @@ -2427,7 +2355,7 @@ int main(int argc, char *argv[]) { if (arg_follow) { poll_fd = sd_journal_get_fd(j); if (poll_fd == -EMFILE) { - log_warning_errno(poll_fd, "Insufficent watch descriptors available. Reverting to -n."); + log_warning_errno(poll_fd, "Insufficient watch descriptors available. Reverting to -n."); arg_follow = false; } else if (poll_fd == -EMEDIUMTYPE) { log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN."); diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index accbad4180..71d9282ed5 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -445,7 +445,7 @@ void server_process_audit_message( if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR)) return; - /* Except AUDIT_USER, all messsages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */ + /* Except AUDIT_USER, all messages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */ if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG && nl->nlmsg_type != AUDIT_USER) return; diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index 7c51f2f633..e6aeb03b77 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -48,7 +48,7 @@ * previously had trouble associating the log message with the service. * * NB: With and without the metadata cache: the implicitly added entry metadata in the journal (with the exception of - * UID/PID/GID and SELinux label) must be understood as possibly slightly out of sync (i.e. sometimes slighly older + * UID/PID/GID and SELinux label) must be understood as possibly slightly out of sync (i.e. sometimes slightly older * and sometimes slightly newer than what was current at the log event). */ diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index ee6c25541c..51e7dedad5 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -286,13 +286,14 @@ static bool flushed_flag_is_set(void) { return access("/run/systemd/journal/flushed", F_OK) >= 0; } -static int system_journal_open(Server *s, bool flush_requested) { +static int system_journal_open(Server *s, bool flush_requested, bool relinquish_requested) { const char *fn; int r = 0; if (!s->system_journal && IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && - (flush_requested || flushed_flag_is_set())) { + (flush_requested || flushed_flag_is_set()) && + !relinquish_requested) { /* If in auto mode: first try to create the machine * path, but not the prefix. @@ -334,7 +335,7 @@ static int system_journal_open(Server *s, bool flush_requested) { fn = strjoina(s->runtime_storage.path, "/system.journal"); - if (s->system_journal) { + if (s->system_journal && !relinquish_requested) { /* Try to open the runtime journal, but only * if it already exists, so that we can flush @@ -389,7 +390,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { * else that's left the journals as NULL). * * Fixes https://github.com/systemd/systemd/issues/3968 */ - (void) system_journal_open(s, false); + (void) system_journal_open(s, false, false); /* We split up user logs only on /var, not on /run. If the * runtime file is open, we write to it exclusively, in order @@ -1117,7 +1118,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) { char ts[FORMAT_TIMESPAN_MAX]; usec_t start; unsigned n = 0; - int r; + int r, k; assert(s); @@ -1130,7 +1131,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) { if (require_flag_file && !flushed_flag_is_set()) return 0; - (void) system_journal_open(s, true); + (void) system_journal_open(s, true, false); if (!s->system_journal) return 0; @@ -1209,9 +1210,36 @@ finish: n), NULL); + k = touch("/run/systemd/journal/flushed"); + if (k < 0) + log_warning_errno(k, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); + return r; } +static int server_relinquish_var(Server *s) { + assert(s); + + if (s->storage == STORAGE_NONE) + return 0; + + if (s->runtime_journal && !s->system_journal) + return 0; + + log_debug("Relinquishing /var..."); + + (void) system_journal_open(s, false, true); + + s->system_journal = journal_file_close(s->system_journal); + ordered_hashmap_clear_with_destructor(s->user_journals, journal_file_close); + set_clear_with_destructor(s->deferred_closes, journal_file_close); + + if (unlink("/run/systemd/journal/flushed") < 0 && errno != ENOENT) + log_warning_errno(errno, "Failed to unlink /run/systemd/journal/flushed, ignoring: %m"); + + return 0; +} + int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) { Server *s = userdata; struct ucred *ucred = NULL; @@ -1330,33 +1358,32 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void return 0; } -static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { - Server *s = userdata; - int r; - +static void server_full_flush(Server *s) { assert(s); - log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid); - (void) server_flush_to_var(s, false); server_sync(s); server_vacuum(s, false); - r = touch("/run/systemd/journal/flushed"); - if (r < 0) - log_warning_errno(r, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); - server_space_usage_message(s, NULL); - return 0; } -static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { +static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { Server *s = userdata; + + assert(s); + + log_info("Received SIGUSR1 signal from PID " PID_FMT ", as request to flush runtime journal.", si->ssi_pid); + server_full_flush(s); + + return 0; +} + +static void server_full_rotate(Server *s) { int r; assert(s); - log_info("Received request to rotate journal from PID " PID_FMT, si->ssi_pid); server_rotate(s); server_vacuum(s, true); @@ -1369,6 +1396,15 @@ static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo * r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC)); if (r < 0) log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m"); +} + +static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + Server *s = userdata; + + assert(s); + + log_info("Received SIGUSR2 signal from PID " PID_FMT ", as request to rotate journal.", si->ssi_pid); + server_full_rotate(s); return 0; } @@ -1384,14 +1420,11 @@ static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo * return 0; } -static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { - Server *s = userdata; +static void server_full_sync(Server *s) { int r; assert(s); - log_debug("Received request to sync from PID " PID_FMT, si->ssi_pid); - server_sync(s); /* Let clients know when the most recent sync happened. */ @@ -1399,6 +1432,17 @@ static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo if (r < 0) log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m"); + return; +} + +static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + Server *s = userdata; + + assert(s); + + log_debug("Received SIGRTMIN1 signal from PID " PID_FMT ", as request to sync.", si->ssi_pid ); + server_full_sync(s); + return 0; } @@ -1803,6 +1847,151 @@ static int server_connect_notify(Server *s) { return 0; } +static int synchronize_second_half(sd_event_source *event_source, void *userdata) { + Varlink *link = userdata; + Server *s; + int r; + + assert(link); + assert_se(s = varlink_get_userdata(link)); + + /* This is the "second half" of the Synchronize() varlink method. This function is called as deferred + * event source at a low priority to ensure the synchronization completes after all queued log + * messages are processed. */ + server_full_sync(s); + + /* Let's get rid of the event source now, by marking it as non-floating again. It then has no ref + * anymore and is immediately destroyed after we return from this function, i.e. from this event + * source handler at the end. */ + r = sd_event_source_set_floating(event_source, false); + if (r < 0) + return log_error_errno(r, "Failed to mark event source as non-floating: %m"); + + return varlink_reply(link, NULL); +} + +static void synchronize_destroy(void *userdata) { + varlink_unref(userdata); +} + +static int vl_method_synchronize(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + _cleanup_(sd_event_source_unrefp) sd_event_source *event_source = NULL; + Server *s = userdata; + int r; + + assert(link); + assert(s); + + if (json_variant_elements(parameters) > 0) + return varlink_error_invalid_parameter(link, parameters); + + log_info("Received client request to rotate journal."); + + /* We don't do the main work now, but instead enqueue a deferred event loop job which will do + * it. That job is scheduled at low priority, so that we return from this method call only after all + * queued but not processed log messages are written to disk, so that this method call returning can + * be used as nice synchronization point. */ + r = sd_event_add_defer(s->event, &event_source, synchronize_second_half, link); + if (r < 0) + return log_error_errno(r, "Failed to allocate defer event source: %m"); + + r = sd_event_source_set_destroy_callback(event_source, synchronize_destroy); + if (r < 0) + return log_error_errno(r, "Failed to set event source destroy callback: %m"); + + varlink_ref(link); /* The varlink object is now left to the destroy callack to unref */ + + r = sd_event_source_set_priority(event_source, SD_EVENT_PRIORITY_NORMAL+15); + if (r < 0) + return log_error_errno(r, "Failed to set defer event source priority: %m"); + + /* Give up ownership of this event source. It will now be destroyed along with event loop itself, + * unless it destroys itself earlier. */ + r = sd_event_source_set_floating(event_source, true); + if (r < 0) + return log_error_errno(r, "Failed to mark event source as floating: %m"); + + (void) sd_event_source_set_description(event_source, "deferred-sync"); + + return 0; +} + +static int vl_method_rotate(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + Server *s = userdata; + + assert(link); + assert(s); + + if (json_variant_elements(parameters) > 0) + return varlink_error_invalid_parameter(link, parameters); + + log_info("Received client request to rotate journal."); + server_full_rotate(s); + + return varlink_reply(link, NULL); +} + +static int vl_method_flush_to_var(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + Server *s = userdata; + + assert(link); + assert(s); + + if (json_variant_elements(parameters) > 0) + return varlink_error_invalid_parameter(link, parameters); + + log_info("Received client request to flush runtime journal."); + server_full_flush(s); + + return varlink_reply(link, NULL); +} + +static int vl_method_relinquish_var(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + Server *s = userdata; + + assert(link); + assert(s); + + if (json_variant_elements(parameters) > 0) + return varlink_error_invalid_parameter(link, parameters); + + log_info("Received client request to relinquish /var access."); + server_relinquish_var(s); + + return varlink_reply(link, NULL); +} + +static int server_open_varlink(Server *s) { + int r; + + assert(s); + + r = varlink_server_new(&s->varlink_server, VARLINK_SERVER_ROOT_ONLY); + if (r < 0) + return r; + + varlink_server_set_userdata(s->varlink_server, s); + + r = varlink_server_bind_method_many( + s->varlink_server, + "io.systemd.Journal.Synchronize", vl_method_synchronize, + "io.systemd.Journal.Rotate", vl_method_rotate, + "io.systemd.Journal.FlushToVar", vl_method_flush_to_var, + "io.systemd.Journal.RelinquishVar", vl_method_relinquish_var); + if (r < 0) + return r; + + r = varlink_server_listen_address(s->varlink_server, "/run/systemd/journal/io.systemd.journal", 0600); + if (r < 0) + return r; + + r = varlink_server_attach_event(s->varlink_server, s->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return r; + + return 0; +} + int server_init(Server *s) { _cleanup_fdset_free_ FDSet *fds = NULL; int n, r, fd; @@ -1973,6 +2162,10 @@ int server_init(Server *s) { return r; } + r = server_open_varlink(s); + if (r < 0) + return r; + r = server_open_kernel_seqnum(s); if (r < 0) return r; @@ -2006,7 +2199,7 @@ int server_init(Server *s) { (void) client_context_acquire_default(s); - return system_journal_open(s, false); + return system_journal_open(s, false, false); } void server_maybe_append_tags(Server *s) { @@ -2043,6 +2236,8 @@ void server_done(Server *s) { ordered_hashmap_free_with_destructor(s->user_journals, journal_file_close); + varlink_server_unref(s->varlink_server); + sd_event_source_unref(s->syslog_event_source); sd_event_source_unref(s->native_event_source); sd_event_source_unref(s->stdout_event_source); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 5f0b3dd4ac..6ce9f269ff 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -17,6 +17,7 @@ typedef struct Server Server; #include "list.h" #include "prioq.h" #include "time-util.h" +#include "varlink.h" typedef enum Storage { STORAGE_AUTO, @@ -166,6 +167,8 @@ struct Server { ClientContext *my_context; /* the context of journald itself */ ClientContext *pid1_context; /* the context of PID 1 */ + + VarlinkServer *varlink_server; }; #define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID=")) diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index 6c61f17c7d..102d2fee18 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -811,7 +811,7 @@ void driver2() { for (j=0; j<8; ++j) /*------------------------ for each input bit, */ { - for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */ + for (m=1; m<8; ++m) /*------------- for several possible initvals, */ { for (l=0; l<HASHSTATE; ++l) e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0); diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 2154cf7eac..a112b9f70f 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -612,7 +612,7 @@ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t siz assert(data); hex_buf = hexmem(data, size); - if (hex_buf == NULL) + if (!hex_buf) return -ENOMEM; fprintf(f, "%s=%s\n", key, hex_buf); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 97e1dd3702..6e077c0860 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -88,7 +88,8 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned attempt; + uint64_t attempt; + uint64_t max_attempts; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -520,10 +521,18 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { return 0; } +int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) { + assert_return(client, -EINVAL); + + client->max_attempts = max_attempts; + + return 0; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); - if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) + if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) return -EADDRNOTAVAIL; if (ret) @@ -532,11 +541,13 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { return 0; } -static void client_notify(sd_dhcp_client *client, int event) { +static int client_notify(sd_dhcp_client *client, int event) { assert(client); if (client->callback) - client->callback(client, event, client->userdata); + return client->callback(client, event, client->userdata); + + return 0; } static int client_initialize(sd_dhcp_client *client) { @@ -551,7 +562,7 @@ static int client_initialize(sd_dhcp_client *client) { (void) event_source_disable(client->timeout_t2); (void) event_source_disable(client->timeout_expire); - client->attempt = 1; + client->attempt = 0; client->state = DHCP_STATE_INIT; client->xid = 0; @@ -595,7 +606,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -686,7 +697,7 @@ static int client_message_init( MAY contain the Parameter Request List option. */ /* NOTE: in case that there would be an option to do not send * any PRL at all, the size should be checked before sending */ - if (client->req_opts_size > 0) { + if (client->req_opts_size > 0 && type != DHCP_RELEASE) { r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, client->req_opts_size, client->req_opts); @@ -718,7 +729,7 @@ static int client_message_init( */ /* RFC7844 section 3: SHOULD NOT contain any other option. */ - if (!client->anonymize) { + if (!client->anonymize && type != DHCP_RELEASE) { max_size = htobe16(size); r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, @@ -850,6 +861,41 @@ static int client_send_discover(sd_dhcp_client *client) { return 0; } +static int client_send_release(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + assert(!IN_SET(client->state, DHCP_STATE_STOPPED)); + + r = client_message_init(client, &release, DHCP_RELEASE, + &optlen, &optoffset); + if (r < 0) + return r; + + /* Fill up release IP and MAC */ + release->dhcp.ciaddr = client->lease->address; + memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); + + r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &release->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "RELEASE"); + + return 0; +} + static int client_send_request(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *request = NULL; size_t optoffset, optlen; @@ -1050,10 +1096,12 @@ static int client_timeout_resend( case DHCP_STATE_REQUESTING: case DHCP_STATE_BOUND: - if (client->attempt < 64) - client->attempt *= 2; + if (client->attempt < client->max_attempts) + client->attempt++; + else + goto error; - next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC; + next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC; break; @@ -1077,17 +1125,15 @@ static int client_timeout_resend( r = client_send_discover(client); if (r >= 0) { client->state = DHCP_STATE_SELECTING; - client->attempt = 1; - } else { - if (client->attempt >= 64) - goto error; - } + client->attempt = 0; + } else if (client->attempt >= client->max_attempts) + goto error; break; case DHCP_STATE_SELECTING: r = client_send_discover(client); - if (r < 0 && client->attempt >= 64) + if (r < 0 && client->attempt >= client->max_attempts) goto error; break; @@ -1097,7 +1143,7 @@ static int client_timeout_resend( case DHCP_STATE_RENEWING: case DHCP_STATE_REBINDING: r = client_send_request(client); - if (r < 0 && client->attempt >= 64) + if (r < 0 && client->attempt >= client->max_attempts) goto error; if (client->state == DHCP_STATE_INIT_REBOOT) @@ -1249,7 +1295,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->fd = asynchronous_close(client->fd); client->state = DHCP_STATE_REBINDING; - client->attempt = 1; + client->attempt = 0; r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, client->mac_addr, @@ -1269,7 +1315,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) DHCP_CLIENT_DONT_DESTROY(client); client->state = DHCP_STATE_RENEWING; - client->attempt = 1; + client->attempt = 0; return client_initialize_time_events(client); } @@ -1319,6 +1365,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ sd_dhcp_lease_unref(client->lease); client->lease = TAKE_PTR(lease); + if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0) + return -ENOMSG; + log_dhcp_client(client, "OFFER"); return 0; @@ -1553,7 +1602,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i if (r >= 0) { client->state = DHCP_STATE_REQUESTING; - client->attempt = 1; + client->attempt = 0; r = event_reset_time(client->event, &client->timeout_resend, clock_boottime_or_monotonic(), @@ -1588,7 +1637,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i notify_event = r; client->state = DHCP_STATE_BOUND; - client->attempt = 1; + client->attempt = 0; client->last_addr = client->lease->address; @@ -1844,6 +1893,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { return r; } +int sd_dhcp_client_send_release(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + + client_send_release(client); + + return 0; +} + int sd_dhcp_client_stop(sd_dhcp_client *client) { DHCP_CLIENT_DONT_DESTROY(client); @@ -1927,10 +1984,10 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { .state = DHCP_STATE_INIT, .ifindex = -1, .fd = -1, - .attempt = 1, .mtu = DHCP_DEFAULT_MIN_SIZE, .port = DHCP_PORT_CLIENT, .anonymize = !!anonymize, + .max_attempts = (uint64_t) -1, }; /* NOTE: this could be moved to a function. */ if (anonymize) { diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 08433adb25..185b55e1c5 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -598,6 +598,7 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, LIST_REMOVE(prefix, ra->prefixes, cur); ra->n_prefixes--; + sd_radv_prefix_unref(cur); break; } diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 0431e2c3f5..5f31d24d20 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -418,7 +418,7 @@ static uint8_t test_addr_acq_ack[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static void test_addr_acq_acquired(sd_dhcp_client *client, int event, +static int test_addr_acq_acquired(sd_dhcp_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp_lease *lease; @@ -426,7 +426,7 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, const struct in_addr *addrs; assert_se(client); - assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); + assert_se(IN_SET(event, SD_DHCP_CLIENT_EVENT_IP_ACQUIRE, SD_DHCP_CLIENT_EVENT_SELECTING)); assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); assert_se(lease); @@ -447,6 +447,8 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, printf(" DHCP address acquired\n"); sd_event_exit(e, 0); + + return 0; } static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index 7dc44e5674..7c6c4663f3 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -60,7 +60,7 @@ static struct { unsigned char prefixlen; uint32_t valid; uint32_t preferred; - bool succesful; + bool successful; } prefix[] = { { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 64, @@ -329,7 +329,7 @@ static void test_ra(void) { if (prefix[i].preferred) assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0); - assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful); + assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].successful); assert_se(sd_radv_add_prefix(ra, p, false) < 0); p = sd_radv_prefix_unref(p); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index a9ab0605ce..5ec42e0f1f 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -680,4 +680,5 @@ global: LIBSYSTEMD_243 { global: sd_bus_object_vtable_format; + sd_event_source_disable_unref; } LIBSYSTEMD_241; diff --git a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION index 3110d57913..973a063761 100644 --- a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION +++ b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION @@ -55,7 +55,7 @@ Regarding the framing of dbus2, also see: https://wiki.gnome.org/Projects/GLib/GDBus/Version2 The first four bytes of the header are defined the same way for dbus1 -and dbus2. The first bytes contain the endianess field and the +and dbus2. The first bytes contain the endianness field and the protocol version, so that the remainder of the message can be safely made sense of just by looking at the first 32bit. diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 5f2502076e..f817cf0a85 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -532,7 +532,7 @@ _public_ int sd_bus_get_name_creds( if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) return r; - /* If we got an unknown method error, fall back to the invidual calls... */ + /* If we got an unknown method error, fall back to the individual calls... */ need_separate_calls = true; sd_bus_error_free(&error); diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index dff39cb13f..d5f8c6db64 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -180,7 +180,7 @@ bool member_name_is_valid(const char *p) { * Complex pattern match * This checks whether @a is a 'complex-prefix' of @b, or @b is a * 'complex-prefix' of @a, based on strings that consist of labels with @c as - * spearator. This function returns true if: + * separator. This function returns true if: * - both strings are equal * - either is a prefix of the other and ends with @c * The second rule makes sure that either string needs to be fully included in diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index d7fcb9329b..39610c5d45 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -326,8 +326,8 @@ struct sd_bus { * with enough entropy yet and might delay the boot */ #define BUS_AUTH_TIMEOUT ((usec_t) DEFAULT_TIMEOUT_USEC) -#define BUS_WQUEUE_MAX (192*1024) -#define BUS_RQUEUE_MAX (192*1024) +#define BUS_WQUEUE_MAX (384*1024) +#define BUS_RQUEUE_MAX (384*1024) #define BUS_MESSAGE_SIZE_MAX (128*1024*1024) #define BUS_AUTH_SIZE_MAX (64*1024) diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 427d42f296..774bf10219 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -26,7 +26,7 @@ static int message_append_basic(sd_bus_message *m, char type, const void *p, con static void *adjust_pointer(const void *p, void *old_base, size_t sz, void *new_base) { - if (p == NULL) + if (!p) return NULL; if (old_base == new_base) @@ -924,7 +924,7 @@ _public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) { /* Unset the bus field if neither the user has a reference nor this message is queued. We are careful * to reset the field only after the last reference to the bus is dropped, after all we might keep - * multiple references to the bus, once for each reference kept on outselves. */ + * multiple references to the bus, once for each reference kept on ourselves. */ m->bus = NULL; return message_free(m); diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 3df7c86b89..1c25ba9dea 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -465,9 +465,9 @@ static int synthesize_connected_signal(sd_bus *bus) { /* If enabled, synthesizes a local "Connected" signal mirroring the local "Disconnected" signal. This is called * whenever we fully established a connection, i.e. after the authorization phase, and after receiving the - * Hello() reply. Or in other words, whenver we enter BUS_RUNNING state. + * Hello() reply. Or in other words, whenever we enter BUS_RUNNING state. * - * This is useful so that clients can start doing stuff whenver the connection is fully established in a way + * This is useful so that clients can start doing stuff whenever the connection is fully established in a way * that works independently from whether we connected to a full bus or just a direct connection. */ if (!bus->connected_signal) @@ -1204,7 +1204,7 @@ _public_ int sd_bus_open_with_description(sd_bus **ret, const char *description) assert_return(ret, -EINVAL); /* Let's connect to the starter bus if it is set, and - * otherwise to the bus that is appropropriate for the scope + * otherwise to the bus that is appropriate for the scope * we are running in */ e = secure_getenv("DBUS_STARTER_BUS_TYPE"); @@ -3869,7 +3869,7 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template * For each matched label, the *decoded* label is stored in the * passed output argument, and the caller is responsible to free * it. Note that the output arguments are only modified if the - * actualy path matched the template. Otherwise, they're left + * actually path matched the template. Otherwise, they're left * untouched. * * This function returns <0 on error, 0 if the path does not match the diff --git a/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c b/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c index f6506fb93f..17598f3d63 100644 --- a/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c +++ b/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c @@ -24,7 +24,7 @@ static int test_ref_unref(void) { m = sd_bus_message_unref(m); bus = sd_bus_unref(bus); - /* We should have a memory leak now on <= v240. Let's do this again, but destory in the opposite + /* We should have a memory leak now on <= v240. Let's do this again, but destroy in the opposite * order. On v240 that too should be a leak. */ r = sd_bus_open_system(&bus); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 1987f279eb..09285c19d8 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -339,6 +339,12 @@ fail: DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free); +_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) { + if (s) + (void) sd_event_source_set_enabled(s, SD_EVENT_OFF); + return sd_event_source_unref(s); +} + static bool event_pid_changed(sd_event *e) { assert(e); @@ -3022,7 +3028,7 @@ static void event_close_inode_data_fds(sd_event *e) { /* Close the fds pointing to the inodes to watch now. We need to close them as they might otherwise pin * filesystems. But we can't close them right-away as we need them as long as the user still wants to make - * adjustments to the even source, such as changing the priority (which requires us to remove and readd a watch + * adjustments to the even source, such as changing the priority (which requires us to remove and re-add a watch * for the inode). Hence, let's close them when entering the first iteration after they were added, as a * compromise. */ diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 118f319a20..9147003b3b 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -170,6 +170,8 @@ static const NLType rtnl_link_info_data_vxlan_types[] = { [IFLA_VXLAN_COLLECT_METADATA] = { .type = NETLINK_TYPE_U8 }, [IFLA_VXLAN_LABEL] = { .type = NETLINK_TYPE_U32 }, [IFLA_VXLAN_GPE] = { .type = NETLINK_TYPE_FLAG }, + [IFLA_VXLAN_TTL_INHERIT] = { .type = NETLINK_TYPE_FLAG }, + [IFLA_VXLAN_DF] = { .type = NETLINK_TYPE_U8 }, }; static const NLType rtnl_bond_arp_target_types[] = { @@ -299,6 +301,8 @@ static const NLType rtnl_link_info_data_geneve_types[] = { [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, [IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GENEVE_TTL_INHERIT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_DF] = { .type = NETLINK_TYPE_U8 }, }; static const NLType rtnl_link_info_data_can_types[] = { @@ -333,6 +337,7 @@ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", + [NL_UNION_LINK_INFO_DATA_IPVTAP] = "ipvtap", [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre", @@ -371,6 +376,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = { .types = rtnl_link_info_data_macvlan_types }, [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), .types = rtnl_link_info_data_ipvlan_types }, + [NL_UNION_LINK_INFO_DATA_IPVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), + .types = rtnl_link_info_data_ipvlan_types }, [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), .types = rtnl_link_info_data_vxlan_types }, [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), @@ -583,20 +590,22 @@ static const NLTypeSystem rtnl_address_type_system = { /* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ static const NLType rtnl_route_metrics_types[] = { - [RTAX_MTU] = { .type = NETLINK_TYPE_U32 }, - [RTAX_WINDOW] = { .type = NETLINK_TYPE_U32 }, - [RTAX_RTT] = { .type = NETLINK_TYPE_U32 }, - [RTAX_RTTVAR] = { .type = NETLINK_TYPE_U32 }, - [RTAX_SSTHRESH] = { .type = NETLINK_TYPE_U32 }, - [RTAX_CWND] = { .type = NETLINK_TYPE_U32 }, - [RTAX_ADVMSS] = { .type = NETLINK_TYPE_U32 }, - [RTAX_REORDERING] = { .type = NETLINK_TYPE_U32 }, - [RTAX_HOPLIMIT] = { .type = NETLINK_TYPE_U32 }, - [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 }, - [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 }, - [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 }, - [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 }, - [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 }, + [RTAX_MTU] = { .type = NETLINK_TYPE_U32 }, + [RTAX_WINDOW] = { .type = NETLINK_TYPE_U32 }, + [RTAX_RTT] = { .type = NETLINK_TYPE_U32 }, + [RTAX_RTTVAR] = { .type = NETLINK_TYPE_U32 }, + [RTAX_SSTHRESH] = { .type = NETLINK_TYPE_U32 }, + [RTAX_CWND] = { .type = NETLINK_TYPE_U32 }, + [RTAX_ADVMSS] = { .type = NETLINK_TYPE_U32 }, + [RTAX_REORDERING] = { .type = NETLINK_TYPE_U32 }, + [RTAX_HOPLIMIT] = { .type = NETLINK_TYPE_U32 }, + [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 }, + [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 }, + [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 }, + [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 }, + [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 }, + [RTAX_CC_ALGO] = { .type = NETLINK_TYPE_U32 }, + [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 }, }; static const NLTypeSystem rtnl_route_metrics_type_system = { @@ -794,6 +803,12 @@ static const NLType genl_fou_types[] = { [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 }, [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 }, [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, + [FOU_ATTR_LOCAL_V4] = { .type = NETLINK_TYPE_IN_ADDR }, + [FOU_ATTR_PEER_V4] = { .type = NETLINK_TYPE_IN_ADDR }, + [FOU_ATTR_LOCAL_V6] = { .type = NETLINK_TYPE_IN_ADDR }, + [FOU_ATTR_PEER_V6] = { .type = NETLINK_TYPE_IN_ADDR}, + [FOU_ATTR_PEER_PORT] = { .type = NETLINK_TYPE_U16}, + [FOU_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32}, }; static const NLTypeSystem genl_fou_type_system = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index a2b3087d15..8585280463 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -62,6 +62,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_MACVLAN, NL_UNION_LINK_INFO_DATA_MACVTAP, NL_UNION_LINK_INFO_DATA_IPVLAN, + NL_UNION_LINK_INFO_DATA_IPVTAP, NL_UNION_LINK_INFO_DATA_VXLAN, NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL, diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index 70036f5136..5f780e0be9 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -49,7 +49,7 @@ static MonitorNetlinkGroup monitor_netlink_group_from_string(const char *name) { * source. Valid sources identifiers are "udev" and "kernel". * * Applications should usually not connect directly to the - * "kernel" events, because the devices might not be useable + * "kernel" events, because the devices might not be usable * at that time, before udev has configured them, and created * device nodes. Accessing devices at the same time as udev, * might result in unpredictable behavior. The "udev" events diff --git a/src/locale/localectl.c b/src/locale/localectl.c index c8b195d9a6..9fb5152110 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -510,6 +510,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 47fd9e874d..d96c8195a2 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -270,6 +270,7 @@ static int run(int argc, char *argv[]) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 342ac56d90..687a534f7b 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -94,7 +94,7 @@ static int show_table(Table *table, const char *word) { assert(table); assert(word); - if (table_get_rows(table) > 1) { + if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) return log_error_errno(r, "Failed to sort table: %m"); @@ -1529,6 +1529,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 2467da18ee..dacd3b3d9c 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -693,8 +693,7 @@ bool manager_all_buttons_ignored(Manager *m) { return false; if (m->handle_lid_switch != HANDLE_IGNORE) return false; - if (m->handle_lid_switch_ep != _HANDLE_ACTION_INVALID && - m->handle_lid_switch_ep != HANDLE_IGNORE) + if (!IN_SET(m->handle_lid_switch_ep, _HANDLE_ACTION_INVALID, HANDLE_IGNORE)) return false; if (m->handle_lid_switch_docked != HANDLE_IGNORE) return false; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 2cebcce123..be767186ff 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -830,7 +830,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus } while (hashmap_get(m->sessions, id)); } - /* If we are not watching utmp aleady, try again */ + /* If we are not watching utmp already, try again */ manager_reconnect_utmp(m); r = manager_add_user_by_uid(m, uid, &user); diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c index 7c79a4afdc..3e2ff6d5b8 100644 --- a/src/login/logind-session-device.c +++ b/src/login/logind-session-device.c @@ -156,7 +156,7 @@ static int session_device_open(SessionDevice *sd, bool active) { case DEVICE_TYPE_UNKNOWN: default: - /* fallback for devices wihout synchronizations */ + /* fallback for devices without synchronizations */ break; } diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 8356a9089a..c5d442865c 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -369,7 +369,7 @@ int user_start(User *u) { return 0; /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear - * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine + * that flag before queueing the start-jobs again. If they succeed, the user object can be re-used just fine * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop() * so possibly pending units are stopped. */ u->stopping = false; @@ -754,7 +754,7 @@ void user_update_last_session_timer(User *u) { assert(!u->timer_event_source); - if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY) + if (IN_SET(u->manager->user_stop_delay, 0, USEC_INFINITY)) return; if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) { diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 2bf3b7987e..a26895e695 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -327,7 +327,7 @@ static const char* getenv_harder(pam_handle_t *handle, const char *key, const ch assert(handle); assert(key); - /* Looks for an environment variable, preferrably in the environment block associated with the + /* Looks for an environment variable, preferably in the environment block associated with the * specified PAM handle, falling back to the process' block instead. Why check both? Because we want * to permit configuration of session properties from unit files that invoke PAM services, so that * PAM services don't have to be reworked to set systemd-specific properties, but these properties diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index d98027a0ca..12de188239 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -255,7 +255,7 @@ static int show_table(Table *table, const char *word) { assert(table); assert(word); - if (table_get_rows(table) > 1) { + if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) return log_error_errno(r, "Failed to sort table: %m"); @@ -3100,6 +3100,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index b290095b0e..43e216acad 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -17,6 +17,7 @@ #include "format-util.h" #include "fs-util.h" #include "fstab-util.h" +#include "libmount-util.h" #include "main-func.h" #include "mount-util.h" #include "mountpoint-util.h" @@ -713,9 +714,11 @@ static int start_transient_automount( } static int find_mount_points(const char *what, char ***list) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; _cleanup_strv_free_ char **l = NULL; size_t bufsize = 0, n = 0; + int r; assert(what); assert(list); @@ -723,55 +726,42 @@ static int find_mount_points(const char *what, char ***list) { /* Returns all mount points obtained from /proc/self/mountinfo in *list, * and the number of mount points as return value. */ - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return log_error_errno(errno, "Can't open /proc/self/mountinfo: %m"); + r = libmount_parse(NULL, NULL, &table, &iter); + if (r < 0) + return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); for (;;) { - _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL; - int r; + struct libmnt_fs *fs; + const char *source, *target; - r = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%ms" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path, &dev); - if (r != 2) { - if (r == EOF) - break; - - continue; - } + r = mnt_table_next_fs(table, iter, &fs); + if (r == 1) + break; + if (r < 0) + return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); - if (!streq(what, dev)) + source = mnt_fs_get_source(fs); + target = mnt_fs_get_target(fs); + if (!source || !target) continue; - r = cunescape(path, UNESCAPE_RELAX, &where); - if (r < 0) + if (!path_equal(source, what)) continue; /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) + if (!GREEDY_REALLOC0(l, bufsize, n + 2)) return log_oom(); - l[n++] = TAKE_PTR(where); + l[n] = strdup(target); + if (!l[n]) + return log_oom(); + n++; } - if (!GREEDY_REALLOC(l, bufsize, n + 1)) + if (!GREEDY_REALLOC0(l, bufsize, n + 1)) return log_oom(); - l[n] = NULL; *list = TAKE_PTR(l); - return n; } @@ -1525,6 +1515,7 @@ static int run(int argc, char* argv[]) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/network/meson.build b/src/network/meson.build index 2acbe858bb..0bcf7f4a81 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -49,6 +49,8 @@ sources = files(''' networkd-address.h networkd-brvlan.c networkd-brvlan.h + networkd-can.c + networkd-can.h networkd-conf.c networkd-conf.h networkd-dhcp4.c @@ -61,6 +63,8 @@ sources = files(''' networkd-link-bus.c networkd-link.c networkd-link.h + networkd-lldp-rx.c + networkd-lldp-rx.h networkd-lldp-tx.c networkd-lldp-tx.h networkd-manager-bus.c diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c index 166aa37139..240cfe34e2 100644 --- a/src/network/netdev/bond.c +++ b/src/network/netdev/bond.c @@ -7,6 +7,8 @@ #include "conf-parser.h" #include "ether-addr-util.h" #include "extract-word.h" +#include "netlink-util.h" +#include "networkd-manager.h" #include "string-table.h" #include "string-util.h" @@ -291,6 +293,78 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin return 0; } +static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set bonding interface: %m"); + return 1; + } + + return 1; +} + +int link_set_bond(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->network); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->network->bond->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); + if (r < 0) + return log_link_error_errno(link, r, "Could not set netlink flags: %m"); + + r = sd_netlink_message_open_container(req, IFLA_LINKINFO); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); + + r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, "bond"); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + if (link->network->active_slave) { + r = sd_netlink_message_append_u32(req, IFLA_BOND_ACTIVE_SLAVE, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BOND_ACTIVE_SLAVE attribute: %m"); + } + + if (link->network->primary_slave) { + r = sd_netlink_message_append_u32(req, IFLA_BOND_PRIMARY, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BOND_PRIMARY attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return r; +} + int config_parse_arp_ip_target_address( const char *unit, const char *filename, diff --git a/src/network/netdev/bond.h b/src/network/netdev/bond.h index 12f59cd946..28796a3a8b 100644 --- a/src/network/netdev/bond.h +++ b/src/network/netdev/bond.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <netinet/in.h> #include <linux/if_bonding.h> #include "in-addr-util.h" @@ -119,6 +120,8 @@ typedef struct Bond { DEFINE_NETDEV_CAST(BOND, Bond); extern const NetDevVTable bond_vtable; +int link_set_bond(Link *link); + const char *bond_mode_to_string(BondMode d) _const_; BondMode bond_mode_from_string(const char *d) _pure_; diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index aadb3ab905..f20130d264 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -5,10 +5,23 @@ #include "missing.h" #include "netlink-util.h" #include "netdev/bridge.h" +#include "network-internal.h" #include "networkd-manager.h" +#include "string-table.h" #include "vlan-util.h" -/* callback for brige netdev's parameter set */ +static const char* const multicast_router_table[_MULTICAST_ROUTER_MAX] = { + [MULTICAST_ROUTER_NONE] = "no", + [MULTICAST_ROUTER_TEMPORARY_QUERY] = "query", + [MULTICAST_ROUTER_PERMANENT] = "permanent", + [MULTICAST_ROUTER_TEMPORARY] = "temporary", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(multicast_router, MulticastRouter, _MULTICAST_ROUTER_INVALID); +DEFINE_CONFIG_PARSE_ENUM(config_parse_multicast_router, multicast_router, MulticastRouter, + "Failed to parse bridge multicast router setting"); + +/* callback for bridge netdev's parameter set */ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; @@ -138,6 +151,142 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return r; } +static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set bridge interface: %m"); + return 1; + } + + return 1; +} + +int link_set_bridge(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->network); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); + if (r < 0) + return log_link_error_errno(link, r, "Could not set message family: %m"); + + r = sd_netlink_message_open_container(req, IFLA_PROTINFO); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); + + if (link->network->use_bpdu >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, link->network->use_bpdu); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m"); + } + + if (link->network->hairpin >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m"); + } + + if (link->network->fast_leave >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); + } + + if (link->network->allow_port_to_be_root >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); + } + + if (link->network->unicast_flood >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); + } + + if (link->network->multicast_flood >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_FLOOD attribute: %m"); + } + + if (link->network->multicast_to_unicast >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_TO_UCAST attribute: %m"); + } + + if (link->network->neighbor_suppression >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_NEIGH_SUPPRESS attribute: %m"); + } + + if (link->network->learning >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_LEARNING attribute: %m"); + } + + if (link->network->bridge_proxy_arp >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP, link->network->bridge_proxy_arp); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP attribute: %m"); + } + + if (link->network->bridge_proxy_arp_wifi >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP_WIFI, link->network->bridge_proxy_arp_wifi); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP_WIFI attribute: %m"); + } + + if (link->network->cost != 0) { + r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); + } + + if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) { + r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m"); + } + + if (link->network->multicast_router != _MULTICAST_ROUTER_INVALID) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MULTICAST_ROUTER, link->network->multicast_router); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MULTICAST_ROUTER attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bridge_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return r; +} + static void bridge_init(NetDev *n) { Bridge *b; diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h index 3edc93a767..2954155c37 100644 --- a/src/network/netdev/bridge.h +++ b/src/network/netdev/bridge.h @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include "netdev/netdev.h" +#include <netinet/in.h> +#include <linux/if_bridge.h> + +#include "conf-parser.h" +#include "netdev.h" typedef struct Bridge { NetDev meta; @@ -20,5 +24,21 @@ typedef struct Bridge { usec_t ageing_time; } Bridge; +typedef enum MulticastRouter { + MULTICAST_ROUTER_NONE = MDB_RTR_TYPE_DISABLED, + MULTICAST_ROUTER_TEMPORARY_QUERY = MDB_RTR_TYPE_TEMP_QUERY, + MULTICAST_ROUTER_PERMANENT = MDB_RTR_TYPE_PERM, + MULTICAST_ROUTER_TEMPORARY = MDB_RTR_TYPE_TEMP, + _MULTICAST_ROUTER_MAX, + _MULTICAST_ROUTER_INVALID = -1, +} MulticastRouter; + DEFINE_NETDEV_CAST(BRIDGE, Bridge); extern const NetDevVTable bridge_vtable; + +int link_set_bridge(Link *link); + +const char* multicast_router_to_string(MulticastRouter i) _const_; +MulticastRouter multicast_router_from_string(const char *s) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_multicast_router); diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c index 2d4481e060..6ce2e5aec9 100644 --- a/src/network/netdev/fou-tunnel.c +++ b/src/network/netdev/fou-tunnel.c @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <arpa/inet.h> +#include <linux/fou.h> #include <net/if.h> +#include <netinet/in.h> #include <linux/ip.h> #include "conf-parser.h" @@ -69,6 +70,26 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m"); + if (t->local_family == AF_INET) { + r = sd_netlink_message_append_in_addr(m, FOU_ATTR_LOCAL_V4, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V4 attribute: %m"); + } else { + r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_LOCAL_V6, &t->local.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V6 attribute: %m"); + } + + if (t->peer_family == AF_INET) { + r = sd_netlink_message_append_in_addr(m, FOU_ATTR_PEER_V4, &t->peer.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V4 attribute: %m"); + } else { + r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_PEER_V6, &t->peer.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V6 attribute: %m"); + } + *ret = TAKE_PTR(m); return 0; } @@ -150,6 +171,41 @@ int config_parse_ip_protocol( return 0; } +int config_parse_fou_tunnel_address( + 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) { + + union in_addr_union *addr = data; + FouTunnel *t = userdata; + int r, *f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(lvalue, "Local")) + f = &t->local_family; + else + f = &t->peer_family; + + r = in_addr_from_string_auto(rvalue, f, addr); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, + "Foo over UDP tunnel '%s' address is invalid, ignoring assignment: %s", + lvalue, rvalue); + + return 0; +} + static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) { FouTunnel *t; diff --git a/src/network/netdev/fou-tunnel.h b/src/network/netdev/fou-tunnel.h index 51eeac41bd..0402239c69 100644 --- a/src/network/netdev/fou-tunnel.h +++ b/src/network/netdev/fou-tunnel.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <netinet/in.h> #include <linux/fou.h> #include "in-addr-util.h" @@ -21,7 +22,12 @@ typedef struct FouTunnel { uint16_t port; + int local_family; + int peer_family; + FooOverUDPEncapType fou_encap_type; + union in_addr_union local; + union in_addr_union peer; } FouTunnel; DEFINE_NETDEV_CAST(FOU, FouTunnel); @@ -32,3 +38,4 @@ FooOverUDPEncapType fou_encap_type_from_string(const char *d) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_fou_encap_type); CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol); +CONFIG_PARSER_PROTOTYPE(config_parse_fou_tunnel_address); diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index 0fb09961d6..3dc8f083cc 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -10,6 +10,7 @@ #include "geneve.h" #include "netlink-util.h" #include "parse-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "missing.h" @@ -18,6 +19,15 @@ #define GENEVE_FLOW_LABEL_MAX_MASK 0xFFFFFU #define DEFAULT_GENEVE_DESTINATION_PORT 6081 +static const char* const geneve_df_table[_NETDEV_GENEVE_DF_MAX] = { + [NETDEV_GENEVE_DF_NO] = "no", + [NETDEV_GENEVE_DF_YES] = "yes", + [NETDEV_GENEVE_DF_INHERIT] = "inherit", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(geneve_df, GeneveDF, NETDEV_GENEVE_DF_YES); +DEFINE_CONFIG_PARSE_ENUM(config_parse_geneve_df, geneve_df, GeneveDF, "Failed to parse Geneve IPDoNotFragment= setting"); + /* callback for geneve netdev's created without a backing Link */ static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; @@ -92,7 +102,11 @@ static int netdev_geneve_create(NetDev *netdev) { return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_REMOTE/IFLA_GENEVE_REMOTE6 attribute: %m"); } - if (v->ttl > 0) { + if (v->inherit) { + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL_INHERIT, 1); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL_INHERIT attribute: %m"); + } else { r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL attribute: %m"); @@ -126,6 +140,12 @@ static int netdev_geneve_create(NetDev *netdev) { return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_LABEL attribute: %m"); } + if (v->geneve_df != _NETDEV_GENEVE_DF_INVALID) { + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_DF, v->geneve_df); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_DF attribute: %m"); + } + r = sd_netlink_message_close_container(m); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); @@ -255,6 +275,47 @@ int config_parse_geneve_flow_label(const char *unit, return 0; } +int config_parse_geneve_ttl(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) { + Geneve *v = userdata; + unsigned f; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "inherit")) + v->inherit = true; + else { + r = safe_atou(rvalue, &f); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse Geneve TTL '%s', ignoring assignment: %m", rvalue); + return 0; + } + + if (f > 255) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid Geneve TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue); + return 0; + } + + v->ttl = f; + } + + return 0; +} + static int netdev_geneve_verify(NetDev *netdev, const char *filename) { Geneve *v = GENEVE(netdev); @@ -262,10 +323,10 @@ static int netdev_geneve_verify(NetDev *netdev, const char *filename) { assert(v); assert(filename); - if (v->ttl == 0) { - log_warning("Invalid Geneve TTL value '0' configured in '%s'. Ignoring", filename); - return -EINVAL; - } + if (v->id > GENEVE_VID_MAX) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: Geneve without valid VNI (or Virtual Network Identifier) configured. Ignoring.", + filename); return 0; } @@ -280,6 +341,7 @@ static void geneve_init(NetDev *netdev) { assert(v); v->id = GENEVE_VID_MAX + 1; + v->geneve_df = _NETDEV_GENEVE_DF_INVALID; v->dest_port = DEFAULT_GENEVE_DESTINATION_PORT; v->udpcsum = false; v->udp6zerocsumtx = false; diff --git a/src/network/netdev/geneve.h b/src/network/netdev/geneve.h index c201981e02..32f7f038ba 100644 --- a/src/network/netdev/geneve.h +++ b/src/network/netdev/geneve.h @@ -10,6 +10,14 @@ typedef struct Geneve Geneve; #define GENEVE_VID_MAX (1u << 24) - 1 +typedef enum GeneveDF { + NETDEV_GENEVE_DF_NO = GENEVE_DF_UNSET, + NETDEV_GENEVE_DF_YES = GENEVE_DF_SET, + NETDEV_GENEVE_DF_INHERIT = GENEVE_DF_INHERIT, + _NETDEV_GENEVE_DF_MAX, + _NETDEV_GENEVE_DF_INVALID = -1 +} GeneveDF; + struct Geneve { NetDev meta; @@ -26,13 +34,20 @@ struct Geneve { bool udpcsum; bool udp6zerocsumtx; bool udp6zerocsumrx; + bool inherit; + GeneveDF geneve_df; union in_addr_union remote; }; DEFINE_NETDEV_CAST(GENEVE, Geneve); extern const NetDevVTable geneve_vtable; +const char *geneve_df_to_string(GeneveDF d) _const_; +GeneveDF geneve_df_from_string(const char *d) _pure_; + CONFIG_PARSER_PROTOTYPE(config_parse_geneve_vni); CONFIG_PARSER_PROTOTYPE(config_parse_geneve_address); CONFIG_PARSER_PROTOTYPE(config_parse_geneve_flow_label); +CONFIG_PARSER_PROTOTYPE(config_parse_geneve_df); +CONFIG_PARSER_PROTOTYPE(config_parse_geneve_ttl); diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c index 5bb6a5bb35..7b25176767 100644 --- a/src/network/netdev/ipvlan.c +++ b/src/network/netdev/ipvlan.c @@ -32,7 +32,10 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(link); assert(netdev->ifname); - m = IPVLAN(netdev); + if (netdev->kind == NETDEV_KIND_IPVLAN) + m = IPVLAN(netdev); + else + m = IPVTAP(netdev); assert(m); @@ -56,7 +59,10 @@ static void ipvlan_init(NetDev *n) { assert(n); - m = IPVLAN(n); + if (n->kind == NETDEV_KIND_IPVLAN) + m = IPVLAN(n); + else + m = IPVTAP(n); assert(m); @@ -71,3 +77,11 @@ const NetDevVTable ipvlan_vtable = { .fill_message_create = netdev_ipvlan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, }; + +const NetDevVTable ipvtap_vtable = { + .object_size = sizeof(IPVlan), + .init = ipvlan_init, + .sections = "Match\0NetDev\0IPVTAP\0", + .fill_message_create = netdev_ipvlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, +}; diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h index 78f09dbb2d..140cacf4fc 100644 --- a/src/network/netdev/ipvlan.h +++ b/src/network/netdev/ipvlan.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <netinet/in.h> #include <linux/if_link.h> #include "netdev/netdev.h" @@ -29,7 +30,9 @@ typedef struct IPVlan { } IPVlan; DEFINE_NETDEV_CAST(IPVLAN, IPVlan); +DEFINE_NETDEV_CAST(IPVTAP, IPVlan); extern const NetDevVTable ipvlan_vtable; +extern const NetDevVTable ipvtap_vtable; const char *ipvlan_mode_to_string(IPVlanMode d) _const_; IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c index 2a74d89e5d..6ac23052bb 100644 --- a/src/network/netdev/l2tp-tunnel.c +++ b/src/network/netdev/l2tp-tunnel.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <arpa/inet.h> +#include <netinet/in.h> #include <linux/l2tp.h> #include <linux/genetlink.h> diff --git a/src/network/netdev/l2tp-tunnel.h b/src/network/netdev/l2tp-tunnel.h index a97c924c5d..d314b98703 100644 --- a/src/network/netdev/l2tp-tunnel.h +++ b/src/network/netdev/l2tp-tunnel.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <netinet/in.h> #include <linux/l2tp.h> #include "in-addr-util.h" diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index ccc37cded4..c82a6fd0ac 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <arpa/inet.h> +#include <netinet/in.h> #include <linux/if_ether.h> #include <linux/if_macsec.h> #include <linux/genetlink.h> diff --git a/src/network/netdev/macsec.h b/src/network/netdev/macsec.h index 2bd08ac500..2a3443a6d4 100644 --- a/src/network/netdev/macsec.h +++ b/src/network/netdev/macsec.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <netinet/in.h> #include <linux/if_macsec.h> #include "in-addr-util.h" diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 107827567e..0e712c55ae 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -54,6 +54,8 @@ MACVLAN.Mode, config_parse_macvlan_mode, MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) +IPVTAP.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) +IPVTAP.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) @@ -79,6 +81,8 @@ Tunnel.ISATAP, config_parse_tristate, FooOverUDP.Protocol, config_parse_ip_protocol, 0, offsetof(FouTunnel, fou_protocol) FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) +FooOverUDP.Local, config_parse_fou_tunnel_address, 0, offsetof(FouTunnel, local) +FooOverUDP.Peer, config_parse_fou_tunnel_address, 0, offsetof(FouTunnel, peer) L2TP.TunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, tunnel_id) L2TP.PeerTunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, peer_tunnel_id) L2TP.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport) @@ -96,12 +100,13 @@ L2TPSession.Name, config_parse_l2tp_session_name, Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.VNI, config_parse_uint32, 0, offsetof(VxLan, vni) +VXLAN.Id, config_parse_uint32, 0, offsetof(VxLan, vni) /* deprecated */ +VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, group) VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.TTL, config_parse_vxlan_ttl, 0, offsetof(VxLan, ttl) VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) @@ -118,20 +123,23 @@ VXLAN.RemoteChecksumTx, config_parse_bool, VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.GenericProtocolExtension, config_parse_bool, 0, offsetof(VxLan, generic_protocol_extension) VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) VXLAN.PortRange, config_parse_port_range, 0, 0 VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) VXLAN.FlowLabel, config_parse_flow_label, 0, 0 +VXLAN.IPDoNotFragment, config_parse_df, 0, offsetof(VxLan, df) GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) -GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) +GENEVE.TTL, config_parse_geneve_ttl, 0, offsetof(Geneve, ttl) GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) GENEVE.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) GENEVE.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port) +GENEVE.IPDoNotFragment, config_parse_geneve_df, 0, offsetof(Geneve, geneve_df) GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0 MACsec.Port, config_parse_macsec_port, 0, 0 MACsec.Encrypt, config_parse_tristate, 0, offsetof(MACsec, encrypt) @@ -197,7 +205,8 @@ Bridge.VLANFiltering, config_parse_tristate, Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ VRF.Table, config_parse_uint32, 0, offsetof(Vrf, table) -WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) +WireGuard.FirewallMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) +WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) /* deprecated */ WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0 diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index d8b8bca7e8..3968ab5495 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -45,6 +45,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_MACVLAN] = &macvlan_vtable, [NETDEV_KIND_MACVTAP] = &macvtap_vtable, [NETDEV_KIND_IPVLAN] = &ipvlan_vtable, + [NETDEV_KIND_IPVTAP] = &ipvtap_vtable, [NETDEV_KIND_VXLAN] = &vxlan_vtable, [NETDEV_KIND_IPIP] = &ipip_vtable, [NETDEV_KIND_GRE] = &gre_vtable, @@ -78,6 +79,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_MACVLAN] = "macvlan", [NETDEV_KIND_MACVTAP] = "macvtap", [NETDEV_KIND_IPVLAN] = "ipvlan", + [NETDEV_KIND_IPVTAP] = "ipvtap", [NETDEV_KIND_VXLAN] = "vxlan", [NETDEV_KIND_IPIP] = "ipip", [NETDEV_KIND_GRE] = "gre", @@ -254,7 +256,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, link_netlink_message if (link->flags & IFF_UP && netdev->kind == NETDEV_KIND_BOND) { log_netdev_debug(netdev, "Link '%s' was up when attempting to enslave it. Bringing link down.", link->ifname); - r = link_down(link); + r = link_down(link, NULL); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not bring link down: %m"); } @@ -732,7 +734,7 @@ int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->filename) return log_oom(); - if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { + if (!netdev->mac && !IN_SET(netdev->kind, NETDEV_KIND_VLAN, NETDEV_KIND_BRIDGE)) { r = netdev_get_mac(netdev->ifname, &netdev->mac); if (r < 0) return log_netdev_error_errno(netdev, r, diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 29ecead029..dcf072ce94 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -24,6 +24,7 @@ typedef enum NetDevKind { NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP, NETDEV_KIND_IPVLAN, + NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN, NETDEV_KIND_IPIP, NETDEV_KIND_GRE, diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 84f6af8578..e69a009c31 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <arpa/inet.h> +#include <netinet/in.h> #include <linux/fou.h> #include <linux/ip.h> #include <linux/if_tunnel.h> diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index 951138d257..d08acf0c66 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -2,12 +2,12 @@ #include <errno.h> #include <fcntl.h> -#include <linux/if_tun.h> #include <net/if.h> #include <netinet/if_ether.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> +#include <linux/if_tun.h> #include "alloc-util.h" #include "fd-util.h" diff --git a/src/network/netdev/vcan.h b/src/network/netdev/vcan.h index 6f62686d08..671b5cb639 100644 --- a/src/network/netdev/vcan.h +++ b/src/network/netdev/vcan.h @@ -3,6 +3,7 @@ typedef struct VCan VCan; +#include <netinet/in.h> #include <linux/can/netlink.h> #include "netdev/netdev.h" diff --git a/src/network/netdev/veth.c b/src/network/netdev/veth.c index 3ad95ade0f..a0a0b8e39e 100644 --- a/src/network/netdev/veth.c +++ b/src/network/netdev/veth.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <errno.h> -#include <linux/veth.h> #include <net/if.h> +#include <linux/veth.h> #include "sd-netlink.h" diff --git a/src/network/netdev/vlan.c b/src/network/netdev/vlan.c index dd548b338a..4d939ab045 100644 --- a/src/network/netdev/vlan.c +++ b/src/network/netdev/vlan.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <errno.h> -#include <linux/if_vlan.h> #include <net/if.h> +#include <linux/if_vlan.h> #include "netdev/vlan.h" #include "vlan-util.h" diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index 4b855ae1e1..e24537083b 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -7,6 +7,7 @@ #include "conf-parser.h" #include "alloc-util.h" #include "extract-word.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "parse-util.h" @@ -15,6 +16,15 @@ #include "networkd-link.h" #include "netdev/vxlan.h" +static const char* const df_table[_NETDEV_VXLAN_DF_MAX] = { + [NETDEV_VXLAN_DF_NO] = "no", + [NETDEV_VXLAN_DF_YES] = "yes", + [NETDEV_VXLAN_DF_INHERIT] = "inherit", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(df, VxLanDF, NETDEV_VXLAN_DF_YES); +DEFINE_CONFIG_PARSE_ENUM(config_parse_df, df, VxLanDF, "Failed to parse VXLAN IPDoNotFragment= setting"); + static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { VxLan *v; int r; @@ -27,8 +37,8 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli assert(v); - if (v->id <= VXLAN_VID_MAX) { - r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->id); + if (v->vni <= VXLAN_VID_MAX) { + r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); } @@ -55,7 +65,11 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); - if (v->ttl != 0) { + if (v->inherit) { + r = sd_netlink_message_append_flag(m, IFLA_VXLAN_TTL_INHERIT); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL_INHERIT attribute: %m"); + } else { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); @@ -144,6 +158,18 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GBP attribute: %m"); } + if (v->generic_protocol_extension) { + r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GPE); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GPE attribute: %m"); + } + + if (v->df != _NETDEV_VXLAN_DF_INVALID) { + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_DF, v->df); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_DF attribute: %m"); + } + return r; } @@ -180,7 +206,7 @@ int config_parse_vxlan_address(const char *unit, return 0; } - v->remote_family = f; + v->group_family = f; } else { if (r > 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue); @@ -266,6 +292,47 @@ int config_parse_flow_label(const char *unit, return 0; } +int config_parse_vxlan_ttl(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) { + VxLan *v = userdata; + unsigned f; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "inherit")) + v->inherit = true; + else { + r = safe_atou(rvalue, &f); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse VXLAN TTL '%s', ignoring assignment: %m", rvalue); + return 0; + } + + if (f > 255) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid VXLAN TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue); + return 0; + } + + v->ttl = f; + } + + return 0; +} + static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { VxLan *v = VXLAN(netdev); @@ -273,10 +340,18 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { assert(v); assert(filename); - if (v->id > VXLAN_VID_MAX) { - log_warning("VXLAN without valid Id configured in %s. Ignoring", filename); - return -EINVAL; - } + if (v->vni > VXLAN_VID_MAX) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: VXLAN without valid VNI (or VXLAN Segment ID) configured. Ignoring.", + filename); + + if (v->ttl > 255) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: VXLAN TTL must be <= 255. Ignoring.", + filename); + + if (!v->dest_port && v->generic_protocol_extension) + v->dest_port = 4790; return 0; } @@ -290,7 +365,8 @@ static void vxlan_init(NetDev *netdev) { assert(v); - v->id = VXLAN_VID_MAX + 1; + v->vni = VXLAN_VID_MAX + 1; + v->df = _NETDEV_VXLAN_DF_INVALID; v->learning = true; v->udpcsum = false; v->udp6zerocsumtx = false; diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index 3b273e97bd..0c61bba2b8 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -3,22 +3,36 @@ typedef struct VxLan VxLan; +#include <linux/if_link.h> + #include "in-addr-util.h" #include "netdev/netdev.h" #define VXLAN_VID_MAX (1u << 24) - 1 #define VXLAN_FLOW_LABEL_MAX_MASK 0xFFFFFU +typedef enum VxLanDF { + NETDEV_VXLAN_DF_NO = VXLAN_DF_UNSET, + NETDEV_VXLAN_DF_YES = VXLAN_DF_SET, + NETDEV_VXLAN_DF_INHERIT = VXLAN_DF_INHERIT, + _NETDEV_VXLAN_DF_MAX, + _NETDEV_VXLAN_DF_INVALID = -1 +} VxLanDF; + struct VxLan { NetDev meta; - uint64_t id; + uint32_t vni; int remote_family; int local_family; + int group_family; + + VxLanDF df; union in_addr_union remote; union in_addr_union local; + union in_addr_union group; unsigned tos; unsigned ttl; @@ -40,6 +54,8 @@ struct VxLan { bool remote_csum_tx; bool remote_csum_rx; bool group_policy; + bool generic_protocol_extension; + bool inherit; struct ifla_vxlan_port_range port_range; }; @@ -47,6 +63,11 @@ struct VxLan { DEFINE_NETDEV_CAST(VXLAN, VxLan); extern const NetDevVTable vxlan_vtable; +const char *df_to_string(VxLanDF d) _const_; +VxLanDF df_from_string(const char *d) _pure_; + CONFIG_PARSER_PROTOTYPE(config_parse_vxlan_address); CONFIG_PARSER_PROTOTYPE(config_parse_port_range); CONFIG_PARSER_PROTOTYPE(config_parse_flow_label); +CONFIG_PARSER_PROTOTYPE(config_parse_df); +CONFIG_PARSER_PROTOTYPE(config_parse_vxlan_ttl); diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index f3084c0773..91fcee1401 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -401,7 +401,7 @@ static void resolve_endpoints(NetDev *netdev) { WireguardPeer *peer; Wireguard *w; Iterator i; - int r = 0; + int r; assert(netdev); w = WIREGUARD(netdev); @@ -452,22 +452,23 @@ int config_parse_wireguard_listen_port( void *userdata) { uint16_t *s = data; - uint16_t port = 0; int r; assert(rvalue); assert(data); - if (!streq(rvalue, "auto")) { - r = parse_ip_port(rvalue, s); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Invalid port specification, ignoring assignment: %s", rvalue); - return 0; - } + if (isempty(rvalue) || streq(rvalue, "auto")) { + *s = 0; + return 0; + } + + r = parse_ip_port(rvalue, s); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid port specification, ignoring assignment: %s", rvalue); + return 0; } - *s = port; return 0; } diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 862f2a99c4..ce336c985a 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -2,6 +2,7 @@ typedef struct Wireguard Wireguard; +#include <netinet/in.h> #include <linux/wireguard.h> #include "in-addr-util.h" diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 3881aaa42b..d29eb7d0b2 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -320,8 +320,8 @@ static int get_gateway_description( return r; for (m = reply; m; m = sd_netlink_message_next(m)) { - union in_addr_union gw = {}; - struct ether_addr mac = {}; + union in_addr_union gw = IN_ADDR_NULL; + struct ether_addr mac = ETHER_ADDR_NULL; uint16_t type; int ifi, fam; @@ -514,7 +514,7 @@ static int dump_address_labels(sd_netlink *rtnl) { for (m = reply; m; m = sd_netlink_message_next(m)) { _cleanup_free_ char *pretty = NULL; - union in_addr_union prefix = {}; + union in_addr_union prefix = IN_ADDR_NULL; uint8_t prefixlen; uint32_t label; @@ -1128,6 +1128,7 @@ static void warn_networkd_missing(void) { static int run(int argc, char* argv[]) { int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index eaf056d118..db6c1456dc 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -106,7 +106,7 @@ static bool address_pool_prefix_is_taken( } /* And don't clash with configured but un-assigned addresses either */ - LIST_FOREACH(networks, n, p->manager->networks) { + ORDERED_HASHMAP_FOREACH(n, p->manager->networks, i) { Address *a; LIST_FOREACH(addresses, a, n->static_addresses) { diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 42d61cc0e5..600bad474d 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -490,7 +490,7 @@ int address_remove( } static int address_acquire(Link *link, Address *original, Address **ret) { - union in_addr_union in_addr = {}; + union in_addr_union in_addr = IN_ADDR_NULL; struct in_addr broadcast = {}; _cleanup_(address_freep) Address *na = NULL; int r; diff --git a/src/network/networkd-can.c b/src/network/networkd-can.c new file mode 100644 index 0000000000..2df0e888f0 --- /dev/null +++ b/src/network/networkd-can.c @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <net/if.h> +#include <linux/can/netlink.h> + +#include "netlink-util.h" +#include "networkd-can.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "string-util.h" + +static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + /* we warn but don't fail the link, as it may be brought up later */ + log_link_warning_errno(link, r, "Could not bring up interface: %m"); + + return 1; +} + +static int link_up_can(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + + log_link_debug(link, "Bringing CAN link up"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + + log_link_debug(link, "Set link"); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Failed to configure CAN link: %m"); + link_enter_failed(link); + } + + return 1; +} + +static int link_set_can(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Configuring CAN link."); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK); + if (r < 0) + return log_link_error_errno(link, r, "Could not set netlink flags: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_link_error_errno(link, r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) { + struct can_bittiming bt = { + .bitrate = link->network->can_bitrate, + .sample_point = link->network->can_sample_point, + }; + + if (link->network->can_bitrate > UINT32_MAX) { + log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate); + return -ERANGE; + } + + log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate); + if (link->network->can_sample_point > 0) + log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); + else + log_link_debug(link, "Using default sample point"); + + r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); + } + + if (link->network->can_restart_us > 0) { + char time_string[FORMAT_TIMESPAN_MAX]; + uint64_t restart_ms; + + if (link->network->can_restart_us == USEC_INFINITY) + restart_ms = 0; + else + restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC); + + format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC); + + if (restart_ms > UINT32_MAX) { + log_link_error(link, "restart timeout (%s) too big.", time_string); + return -ERANGE; + } + + log_link_debug(link, "Setting restart = %s", time_string); + + r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m"); + } + + if (link->network->can_triple_sampling >= 0) { + struct can_ctrlmode cm = { + .mask = CAN_CTRLMODE_3_SAMPLES, + .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0, + }; + + log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis"); + + r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_link_error_errno(link, r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_link_error_errno(link, r, "Failed to close netlink container: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + if (!(link->flags & IFF_UP)) + return link_up_can(link); + + return 0; +} + +static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_link_warning_errno(link, r, "Could not bring down interface: %m"); + link_enter_failed(link); + return 1; + } + + r = link_set_can(link); + if (r < 0) + link_enter_failed(link); + + return 1; +} + +int link_configure_can(Link *link) { + int r; + + if (streq_ptr(link->kind, "can")) { + /* The CAN interface must be down to configure bitrate, etc... */ + if ((link->flags & IFF_UP)) { + r = link_down(link, link_down_handler); + if (r < 0) { + link_enter_failed(link); + return r; + } + } else { + r = link_set_can(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; + } + + if (!(link->flags & IFF_UP)) { + r = link_up_can(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; +} diff --git a/src/network/networkd-can.h b/src/network/networkd-can.h new file mode 100644 index 0000000000..c744bdfea7 --- /dev/null +++ b/src/network/networkd-can.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +typedef struct Link Link; + +int link_configure_can(Link *link); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 85685d6df7..7907988a9b 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <netinet/ether.h> +#include <netinet/in.h> #include <linux/if.h> #include "alloc-util.h" @@ -214,10 +214,7 @@ static int dhcp_lease_lost(Link *link) { } } } - } - r = address_new(&address); - if (r >= 0) { r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); if (r > 0 && !in4_addr_is_null(&router[0])) { _cleanup_(route_freep) Route *route_gw = NULL; @@ -241,7 +238,10 @@ static int dhcp_lease_lost(Link *link) { route_remove(route, link, NULL); } } + } + r = address_new(&address); + if (r >= 0) { r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); if (r >= 0) { r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); @@ -261,7 +261,7 @@ static int dhcp_lease_lost(Link *link) { r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); if (r >= 0 && link->original_mtu != mtu) { - r = link_set_mtu(link, link->original_mtu, true); + r = link_set_mtu(link, link->original_mtu); if (r < 0) { log_link_warning(link, "DHCP error: could not reset MTU"); @@ -308,6 +308,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * link_set_dhcp_routes(link); + /* Add back static routes since kernel removes while DHCPv4 address is removed from when lease expires */ + link_request_set_routes(link); + if (link->dhcp4_messages == 0) { link->dhcp4_configured = true; link_check_ready(link); @@ -451,7 +454,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_mtu(lease, &mtu); if (r >= 0) { - r = link_set_mtu(link, mtu, true); + r = link_set_mtu(link, mtu); if (r < 0) log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu); } @@ -471,7 +474,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { if (r < 0) log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname); if (r == 1) - log_link_notice(link, "Overlong DCHP hostname received, shortened from '%s' to '%s'", dhcpname, hostname); + log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname); } if (hostname) { @@ -508,31 +511,76 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { return 0; } -static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { + +static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) { + sd_dhcp_lease *lease; + struct in_addr addr; + int r; + + assert(link); + assert(link->network); + assert(client); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DHCP lease: %m"); + + r = sd_dhcp_lease_get_server_identifier(lease, &addr); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to get DHCP server ip address: %m"); + + if (set_contains(link->network->dhcp_black_listed_ip, UINT32_TO_PTR(addr.s_addr))) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 ip '%u.%u.%u.%u' found in black listed ip addresses, ignoring offer", + ADDRESS_FMT_VAL(addr))); + return true; + } + + return false; +} + +static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { Link *link = userdata; - int r = 0; + int r; assert(link); assert(link->network); assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; + return 0; switch (event) { - case SD_DHCP_CLIENT_EVENT_EXPIRED: case SD_DHCP_CLIENT_EVENT_STOP: + + if (link_ipv4ll_fallback_enabled(link)) { + assert(link->ipv4ll); + + log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address"); + + r = sd_ipv4ll_start(link->ipv4ll); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); + } + + if (link->network->dhcp_send_release) + (void) sd_dhcp_client_send_release(client); + + _fallthrough_; + case SD_DHCP_CLIENT_EVENT_EXPIRED: case SD_DHCP_CLIENT_EVENT_IP_CHANGE: + if (link->network->dhcp_critical) { log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); - return; + return 0; } if (link->dhcp_lease) { r = dhcp_lease_lost(link); if (r < 0) { link_enter_failed(link); - return; + return r; } } @@ -540,7 +588,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { r = dhcp_lease_acquired(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } } @@ -549,16 +597,23 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { r = dhcp_lease_renew(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } break; case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: r = dhcp_lease_acquired(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } break; + case SD_DHCP_CLIENT_EVENT_SELECTING: + r = dhcp_server_is_black_listed(link, client); + if (r < 0) + return r; + if (r != 0) + return -ENOMSG; + break; default: if (event < 0) log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); @@ -567,7 +622,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { break; } - return; + return 0; } static int dhcp4_set_hostname(Link *link) { @@ -814,5 +869,11 @@ int dhcp4_configure(Link *link) { return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m"); } + if (link->network->dhcp_max_attempts > 0) { + r = sd_dhcp_client_set_max_attempts(link->dhcp_client, link->network->dhcp_max_attempts); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m"); + } + return dhcp4_set_client_identifier(link); } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 90361c9f4a..afa3e2cfd6 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -3,7 +3,7 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ -#include <netinet/ether.h> +#include <netinet/in.h> #include <linux/if.h> #include "sd-radv.h" @@ -90,7 +90,7 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, if (r < 0 && r != -EEXIST) return r; - r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link); + r = manager_dhcp6_prefix_add(link->manager, prefix, link); if (r < 0) return r; @@ -126,47 +126,33 @@ int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { &lifetime_preferred, &lifetime_valid) >= 0) { _cleanup_free_ char *buf = NULL; - _cleanup_free_ Route *route = NULL; + Route *route; - if (pd_prefix_len > 64) + if (pd_prefix_len >= 64) continue; (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); - if (pd_prefix_len < 64) { - r = route_new(&route); - if (r < 0) { - log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } - - r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } - - route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE); - - r = route_remove(route, link, dhcp6_route_remove_handler); - if (r < 0) { - (void) in_addr_to_string(AF_INET6, - &pd_prefix, &buf); - - log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); + r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; + } - continue; - } + route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE); - log_link_debug(link, "Removing unreachable route %s/%u", - strnull(buf), pd_prefix_len); + r = route_remove(route, link, dhcp6_route_remove_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; } + + log_link_debug(link, "Removing unreachable route %s/%u", + strnull(buf), pd_prefix_len); } return 0; @@ -217,7 +203,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, continue; assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6); - if (assigned_link != NULL && assigned_link != link) + if (assigned_link && assigned_link != link) continue; (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); @@ -229,7 +215,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, strnull(assigned_buf), strnull(buf), pd_prefix_len); - if (assigned_link == NULL) + if (!assigned_link) continue; } else @@ -266,7 +252,6 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { union in_addr_union pd_prefix; uint8_t pd_prefix_len; uint32_t lifetime_preferred, lifetime_valid; - _cleanup_free_ char *buf = NULL; Iterator i = ITERATOR_FIRST; r = sd_dhcp6_client_get_lease(client, &lease); @@ -279,32 +264,23 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { &lifetime_preferred, &lifetime_valid) >= 0) { + _cleanup_free_ char *buf = NULL; + + (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + if (pd_prefix_len > 64) { - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", strnull(buf), pd_prefix_len); continue; } - if (pd_prefix_len < 48) { - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + if (pd_prefix_len < 48) log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", strnull(buf), pd_prefix_len); - } if (pd_prefix_len < 64) { - _cleanup_(route_freep) Route *route = NULL; uint32_t table; - - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); - - r = route_new(&route); - if (r < 0) { - log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } + Route *route; table = link_get_dhcp_route_table(link); @@ -328,7 +304,6 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { log_link_debug(link, "Configuring unreachable route for %s/%u", strnull(buf), pd_prefix_len); - } else log_link_debug(link, "Not adding a blocking route since distributed prefix is /64"); @@ -443,7 +418,7 @@ static int dhcp6_address_change( uint32_t lifetime_valid) { _cleanup_(address_freep) Address *addr = NULL; - char buffer[INET6_ADDRSTRLEN]; + _cleanup_free_ char *buffer = NULL; int r; r = address_new(&addr); @@ -451,7 +426,7 @@ static int dhcp6_address_change( return r; addr->family = AF_INET6; - memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr)); + addr->in_addr.in6 = *ip6_addr; addr->flags = IFA_F_NOPREFIXROUTE; addr->prefixlen = 128; @@ -459,10 +434,10 @@ static int dhcp6_address_change( addr->cinfo.ifa_prefered = lifetime_preferred; addr->cinfo.ifa_valid = lifetime_valid; + (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer); log_link_info(link, "DHCPv6 address %s/%d timeout preferred %d valid %d", - inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)), - addr->prefixlen, lifetime_preferred, lifetime_valid); + strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); r = address_configure(addr, link, dhcp6_address_handler, true); if (r < 0) diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index fa13949538..4ae511fc7a 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -9,14 +9,27 @@ #include "alloc-util.h" #include "conf-parser.h" #include "netdev/bridge.h" +#include "netdev/vxlan.h" #include "netlink-util.h" #include "networkd-fdb.h" #include "networkd-manager.h" +#include "parse-util.h" +#include "string-util.h" +#include "string-table.h" #include "util.h" #include "vlan-util.h" #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U +static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = { + [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use", + [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self", + [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master", + [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router", +}; + +DEFINE_STRING_TABLE_LOOKUP(fdb_ntf_flags, NeighborCacheEntryFlags); + /* create a new FDB entry or get an existing one. */ static int fdb_entry_new_static( Network *network, @@ -64,6 +77,8 @@ static int fdb_entry_new_static( *fdb_entry = (FdbEntry) { .network = network, .mac_addr = TAKE_PTR(mac_addr), + .vni = VXLAN_VID_MAX + 1, + .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF, }; LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry); @@ -102,30 +117,19 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) /* send a request to the kernel to add a FDB entry in its static MAC table. */ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - sd_netlink *rtnl; int r; - uint8_t flags; - Bridge *bridge; assert(link); assert(link->network); assert(link->manager); assert(fdb_entry); - rtnl = link->manager->rtnl; - bridge = BRIDGE(link->network->bridge); - /* create new RTM message */ - r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE); + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE); if (r < 0) return rtnl_log_create_error(r); - if (bridge) - flags = NTF_MASTER; - else - flags = NTF_SELF; - - r = sd_rtnl_message_neigh_set_flags(req, flags); + r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags); if (r < 0) return rtnl_log_create_error(r); @@ -139,14 +143,26 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { return rtnl_log_create_error(r); /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */ - if (0 != fdb_entry->vlan_id) { + if (fdb_entry->vlan_id > 0) { r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id); if (r < 0) return rtnl_log_create_error(r); } + if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) { + r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + } + + if (fdb_entry->vni <= VXLAN_VID_MAX) { + r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m"); + } + /* send message to the kernel to update its internal static MAC table. */ - r = netlink_call_async(rtnl, NULL, req, set_fdb_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -258,3 +274,130 @@ int config_parse_fdb_vlan_id( return 0; } + +int config_parse_fdb_destination( + 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) { + + _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = fdb_entry_new_static(network, filename, section_line, &fdb_entry); + if (r < 0) + return log_oom(); + + r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "FDB destination IP address is invalid, ignoring assignment: %s", + rvalue); + + fdb_entry = NULL; + + return 0; +} + +int config_parse_fdb_vxlan_vni( + 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) { + + _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL; + Network *network = userdata; + uint32_t vni; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = fdb_entry_new_static(network, filename, section_line, &fdb_entry); + if (r < 0) + return log_oom(); + + r = safe_atou32(rvalue, &vni); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s", + rvalue); + return 0; + } + + if (vni > VXLAN_VID_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s", + rvalue); + return 0; + } + + fdb_entry->vni = vni; + fdb_entry = NULL; + + return 0; +} + + +int config_parse_fdb_ntf_flags( + 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) { + + _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL; + Network *network = userdata; + NeighborCacheEntryFlags f; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = fdb_entry_new_static(network, filename, section_line, &fdb_entry); + if (r < 0) + return log_oom(); + + f = fdb_ntf_flags_from_string(rvalue); + if (f < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "FDB failed to parse AssociatedWith=, ignoring assignment: %s", + rvalue); + return 0; + } + + fdb_entry->fdb_ntf_flags = f; + fdb_entry = NULL; + + return 0; +} diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 6b7da2e741..bcdd8ce3cb 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -5,6 +5,8 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ +#include <linux/neighbour.h> + #include "conf-parser.h" #include "list.h" #include "macro.h" @@ -15,13 +17,28 @@ typedef struct FdbEntry FdbEntry; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; +typedef enum NeighborCacheEntryFlags { + NEIGHBOR_CACHE_ENTRY_FLAGS_USE = NTF_USE, + NEIGHBOR_CACHE_ENTRY_FLAGS_SELF = NTF_SELF, + NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER = NTF_MASTER, + NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER = NTF_ROUTER, + _NEIGHBOR_CACHE_ENTRY_FLAGS_MAX, + _NEIGHBOR_CACHE_ENTRY_FLAGS_INVALID = -1, +} NeighborCacheEntryFlags; + struct FdbEntry { Network *network; NetworkConfigSection *section; - struct ether_addr *mac_addr; + uint32_t vni; + + int family; uint16_t vlan_id; + struct ether_addr *mac_addr; + union in_addr_union destination_addr; + NeighborCacheEntryFlags fdb_ntf_flags; + LIST_FIELDS(FdbEntry, static_fdb_entries); }; @@ -30,5 +47,11 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry); DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free); +const char* fdb_ntf_flags_to_string(NeighborCacheEntryFlags i) _const_; +NeighborCacheEntryFlags fdb_ntf_flags_from_string(const char *s) _pure_; + CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id); +CONFIG_PARSER_PROTOTYPE(config_parse_fdb_destination); +CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vxlan_vni); +CONFIG_PARSER_PROTOTYPE(config_parse_fdb_ntf_flags); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 829dc48a0a..9df5646a7e 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <netinet/ether.h> +#include <netinet/in.h> #include <linux/if.h> #include "network-internal.h" @@ -34,7 +34,9 @@ static int ipv4ll_address_lost(Link *link) { address->prefixlen = 16; address->scope = RT_SCOPE_LINK; - address_remove(address, link, NULL); + r = address_remove(address, link, NULL); + if (r < 0) + return r; r = route_new(&route); if (r < 0) @@ -44,7 +46,9 @@ static int ipv4ll_address_lost(Link *link) { route->scope = RT_SCOPE_LINK; route->priority = IPV4LL_ROUTE_METRIC; - route_remove(route, link, NULL); + r = route_remove(route, link, NULL); + if (r < 0) + return r; link_check_ready(link); @@ -175,7 +179,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { r = sd_ipv4ll_restart(ll); if (r < 0) - log_link_warning(link, "Could not acquire IPv4 link-local address"); + log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); break; case SD_IPV4LL_EVENT_BIND: r = ipv4ll_address_claimed(ll, link); @@ -197,7 +201,7 @@ int ipv4ll_configure(Link *link) { assert(link); assert(link->network); - assert(link->network->link_local & ADDRESS_FAMILY_IPV4); + assert(link->network->link_local & (ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)); if (!link->ipv4ll) { r = sd_ipv4ll_new(&link->ipv4ll); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index e2d77e9ad2..863171b071 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <netinet/ether.h> +#include <netinet/in.h> #include <linux/if.h> #include <unistd.h> diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4846b13a8b..6b4ed33932 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <netinet/ether.h> +#include <netinet/in.h> #include <linux/if.h> -#include <linux/can/netlink.h> #include <unistd.h> #include "alloc-util.h" @@ -13,9 +12,12 @@ #include "fd-util.h" #include "fileio.h" #include "missing_network.h" +#include "netdev/bond.h" +#include "netdev/bridge.h" #include "netdev/vrf.h" #include "netlink-util.h" #include "network-internal.h" +#include "networkd-can.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" @@ -110,7 +112,7 @@ static bool link_dhcp4_server_enabled(Link *link) { return link->network->dhcp_server; } -static bool link_ipv4ll_enabled(Link *link) { +bool link_ipv4ll_enabled(Link *link) { assert(link); if (link->flags & IFF_LOOPBACK) @@ -119,7 +121,7 @@ static bool link_ipv4ll_enabled(Link *link) { if (!link->network) return false; - if (STRPTR_IN_SET(link->kind, "vrf", "wireguard")) + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6")) return false; if (link->network->bond) @@ -128,6 +130,24 @@ static bool link_ipv4ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV4; } +bool link_ipv4ll_fallback_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6")) + return false; + + if (link->network->bond) + return false; + + return link->network->link_local & ADDRESS_FAMILY_FALLBACK_IPV4; +} + static bool link_ipv6ll_enabled(Link *link) { assert(link); @@ -140,7 +160,7 @@ static bool link_ipv6ll_enabled(Link *link) { if (!link->network) return false; - if (STRPTR_IN_SET(link->kind, "vrf", "wireguard")) + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti")) return false; if (link->network->bond) @@ -177,42 +197,6 @@ static bool link_radv_enabled(Link *link) { return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE; } -static bool link_lldp_rx_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (link->iftype != ARPHRD_ETHER) - return false; - - if (!link->network) - return false; - - /* LLDP should be handled on bridge slaves as those have a direct - * connection to their peers not on the bridge master. Linux doesn't - * even (by default) forward lldp packets to the bridge master.*/ - if (streq_ptr("bridge", link->kind)) - return false; - - return link->network->lldp_mode != LLDP_MODE_NO; -} - -static bool link_lldp_emit_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (link->iftype != ARPHRD_ETHER) - return false; - - if (!link->network) - return false; - - return link->network->lldp_emit != LLDP_EMIT_NO; -} - static bool link_ipv4_forward_enabled(Link *link) { assert(link); @@ -320,8 +304,7 @@ static int link_enable_ipv6(Link *link) { r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled); if (r < 0) - log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", - enable_disable(!disabled), link->ifname); + log_link_warning_errno(link, r, "Cannot %s IPv6: %m", enable_disable(!disabled)); else log_link_info(link, "IPv6 successfully %sd", enable_disable(!disabled)); @@ -409,7 +392,7 @@ void link_update_operstate(Link *link, bool also_update_master) { if (operstate >= LINK_OPERSTATE_CARRIER) { Link *slave; - HASHMAP_FOREACH(slave, link->slaves, i) { + SET_FOREACH(slave, link->slaves, i) { link_update_operstate(slave, false); if (slave->operstate < LINK_OPERSTATE_CARRIER) @@ -506,8 +489,6 @@ static int link_update_flags(Link *link, sd_netlink_message *m) { return 0; } -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); - static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { _cleanup_(link_unrefp) Link *link = NULL; uint16_t type; @@ -605,40 +586,16 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { return 0; } -static void link_detach_from_manager(Link *link) { - if (!link || !link->manager) - return; - - hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); - set_remove(link->manager->links_requesting_uuid, link); - link_clean(link); -} - static Link *link_free(Link *link) { - Link *carrier, *master; Address *address; - Route *route; - Iterator i; assert(link); - while ((route = set_first(link->routes))) - route_free(route); - - while ((route = set_first(link->routes_foreign))) - route_free(route); + link->routes = set_free_with_destructor(link->routes, route_free); + link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free); - link->routes = set_free(link->routes); - link->routes_foreign = set_free(link->routes_foreign); - - while ((address = set_first(link->addresses))) - address_free(address); - - while ((address = set_first(link->addresses_foreign))) - address_free(address); - - link->addresses = set_free(link->addresses); - link->addresses_foreign = set_free(link->addresses_foreign); + link->addresses = set_free_with_destructor(link->addresses, address_free); + link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); @@ -663,10 +620,7 @@ static Link *link_free(Link *link) { sd_ndisc_unref(link->ndisc); sd_radv_unref(link->radv); - link_detach_from_manager(link); - free(link->ifname); - free(link->kind); (void) unlink(link->state_file); @@ -674,25 +628,12 @@ static Link *link_free(Link *link) { sd_device_unref(link->sd_device); - HASHMAP_FOREACH (carrier, link->bound_to_links, i) - hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex)); hashmap_free(link->bound_to_links); - - HASHMAP_FOREACH (carrier, link->bound_by_links, i) - hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); hashmap_free(link->bound_by_links); - hashmap_free(link->slaves); - - if (link->network) { - if (link->network->bond && - link_get(link->manager, link->network->bond->ifindex, &master) >= 0) - (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex)); + set_free_with_destructor(link->slaves, link_unref); - if (link->network->bridge && - link_get(link->manager, link->network->bridge->ifindex, &master) >= 0) - (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex)); - } + network_unref(link->network); return mfree(link); } @@ -721,6 +662,10 @@ static void link_set_state(Link *link, LinkState state) { if (link->state == state) return; + log_link_debug(link, "State changed: %s -> %s", + link_state_to_string(link->state), + link_state_to_string(state)); + link->state = state; link_send_changed(link, "AdministrativeState", NULL); @@ -736,7 +681,7 @@ static void link_enter_unmanaged(Link *link) { link_dirty(link); } -static int link_stop_clients(Link *link) { +int link_stop_clients(Link *link) { int r = 0, k; assert(link); @@ -931,7 +876,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -static int link_request_set_routes(Link *link) { +int link_request_set_routes(Link *link) { enum { PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */ PHASE_GATEWAY, /* Second phase: Routes with a gateway */ @@ -1004,6 +949,7 @@ void link_check_ready(Link *link) { if (!link->addresses_ready) { link->addresses_ready = true; link_request_set_routes(link); + return; } if (!link->static_routes_configured) @@ -1012,21 +958,18 @@ void link_check_ready(Link *link) { if (!link->routing_policy_rules_configured) return; - if (link_ipv4ll_enabled(link)) - if (!link->ipv4ll_address || - !link->ipv4ll_route) - return; + if (link_ipv4ll_enabled(link) && !(link->ipv4ll_address && link->ipv4ll_route)) + return; if (link_ipv6ll_enabled(link) && in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) return; - if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && - !link->dhcp4_configured) || - (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && - !link->dhcp6_configured) || - (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) && - !link->dhcp4_configured && !link->dhcp6_configured)) + if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && + !(link->dhcp4_configured || link->dhcp6_configured) && + !(link_ipv4ll_fallback_enabled(link) && link->ipv4ll_address && link->ipv4ll_route)) + /* When DHCP is enabled, at least one protocol must provide an address, or + * an IPv4ll fallback address must be configured. */ return; if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) @@ -1160,20 +1103,20 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { log_debug("Copying NTP server information from %s", link->ifname); STRV_FOREACH(a, link->network->ntp) { - struct in_addr ia; + union in_addr_union ia; /* Only look for IPv4 addresses */ - if (inet_pton(AF_INET, *a, &ia) <= 0) + if (in_addr_from_string(AF_INET, *a, &ia) <= 0) continue; /* Never propagate obviously borked data */ - if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) + if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) continue; if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) return log_oom(); - addresses[n_addresses++] = ia; + addresses[n_addresses++] = ia.in; } if (link->network->dhcp_use_ntp && link->dhcp_lease) { @@ -1391,7 +1334,7 @@ static int link_request_set_addresses(Link *link) { } static int link_set_bridge_vlan(Link *link) { - int r = 0; + int r; r = br_vlan_configure(link, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); if (r < 0) @@ -1413,22 +1356,6 @@ static int link_set_proxy_arp(Link *link) { return 0; } -static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - log_link_debug(link, "Set link"); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "Could not join netdev: %m"); - link_enter_failed(link); - } - - return 1; -} - static int link_configure_after_setting_mtu(Link *link); static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1451,13 +1378,13 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) log_link_debug(link, "Setting MTU done."); - if (link->state == LINK_STATE_PENDING) + if (link->state == LINK_STATE_INITIALIZED) (void) link_configure_after_setting_mtu(link); return 1; } -int link_set_mtu(Link *link, uint32_t mtu, bool force) { +int link_set_mtu(Link *link, uint32_t mtu) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -1468,7 +1395,7 @@ int link_set_mtu(Link *link, uint32_t mtu, bool force) { if (mtu == 0 || link->setting_mtu) return 0; - if (force ? link->mtu == mtu : link->mtu >= mtu) + if (link->mtu == mtu) return 0; log_link_debug(link, "Setting MTU: %" PRIu32, mtu); @@ -1506,6 +1433,48 @@ int link_set_mtu(Link *link, uint32_t mtu, bool force) { return 0; } +static bool link_reduces_vlan_mtu(Link *link) { + /* See netif_reduces_vlan_mtu() in kernel. */ + return streq_ptr(link->kind, "macsec"); +} + +static uint32_t link_get_requested_mtu_by_stacked_netdevs(Link *link) { + uint32_t mtu = 0; + NetDev *dev; + Iterator i; + + HASHMAP_FOREACH(dev, link->network->stacked_netdevs, i) + if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0) + /* See vlan_dev_change_mtu() in kernel. */ + mtu = MAX(mtu, link_reduces_vlan_mtu(link) ? dev->mtu + 4 : dev->mtu); + + else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu) + /* See macvlan_change_mtu() in kernel. */ + mtu = dev->mtu; + + return mtu; +} + +static int link_configure_mtu(Link *link) { + uint32_t mtu; + + assert(link); + assert(link->network); + + if (link->network->mtu > 0) + return link_set_mtu(link, link->network->mtu); + + mtu = link_get_requested_mtu_by_stacked_netdevs(link); + if (link->mtu >= mtu) + return 0; + + log_link_notice(link, "Bumping MTU bytes from %"PRIu32" to %"PRIu32" because of stacked device. " + "If it is not desired, then please explicitly specify MTUBytes= setting.", + link->mtu, mtu); + + return link_set_mtu(link, mtu); +} + static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -1575,290 +1544,6 @@ static int link_set_flags(Link *link) { return 0; } -static int link_set_bridge(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->network); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); - if (r < 0) - return log_link_error_errno(link, r, "Could not set message family: %m"); - - r = sd_netlink_message_open_container(req, IFLA_PROTINFO); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); - - if (link->network->use_bpdu >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, link->network->use_bpdu); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m"); - } - - if (link->network->hairpin >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m"); - } - - if (link->network->fast_leave >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); - } - - if (link->network->allow_port_to_be_root >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); - - } - - if (link->network->unicast_flood >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); - } - - if (link->network->multicast_flood >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_FLOOD attribute: %m"); - } - - if (link->network->multicast_to_unicast >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_TO_UCAST attribute: %m"); - } - - if (link->network->neighbor_suppression >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_NEIGH_SUPPRESS attribute: %m"); - } - - if (link->network->learning >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_LEARNING attribute: %m"); - } - - if (link->network->cost != 0) { - r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); - } - - if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) { - r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return r; -} - -static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->ifname); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set bonding interface: %m"); - return 1; - } - - return 1; -} - -static int link_set_bond(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->network); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->network->bond->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); - if (r < 0) - return log_link_error_errno(link, r, "Could not set netlink flags: %m"); - - r = sd_netlink_message_open_container(req, IFLA_LINKINFO); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); - - r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, "bond"); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - if (link->network->active_slave) { - r = sd_netlink_message_append_u32(req, IFLA_BOND_ACTIVE_SLAVE, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BOND_ACTIVE_SLAVE attribute: %m"); - } - - if (link->network->primary_slave) { - r = sd_netlink_message_append_u32(req, IFLA_BOND_PRIMARY, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BOND_PRIMARY attribute: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return r; -} - -static int link_append_to_master(Link *link, NetDev *netdev) { - Link *master; - int r; - - assert(link); - assert(netdev); - - r = link_get(link->manager, netdev->ifindex, &master); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&master->slaves, NULL); - if (r < 0) - return r; - - r = hashmap_put(master->slaves, INT_TO_PTR(link->ifindex), link); - if (r < 0) - return r; - - return 0; -} - -static int link_lldp_save(Link *link) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - sd_lldp_neighbor **l = NULL; - int n = 0, r, i; - - assert(link); - assert(link->lldp_file); - - if (!link->lldp) { - (void) unlink(link->lldp_file); - return 0; - } - - r = sd_lldp_get_neighbors(link->lldp, &l); - if (r < 0) - goto finish; - if (r == 0) { - (void) unlink(link->lldp_file); - goto finish; - } - - n = r; - - r = fopen_temporary(link->lldp_file, &f, &temp_path); - if (r < 0) - goto finish; - - fchmod(fileno(f), 0644); - - for (i = 0; i < n; i++) { - const void *p; - le64_t u; - size_t sz; - - r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); - if (r < 0) - goto finish; - - u = htole64(sz); - (void) fwrite(&u, 1, sizeof(u), f); - (void) fwrite(p, 1, sz, f); - } - - r = fflush_and_check(f); - if (r < 0) - goto finish; - - if (rename(temp_path, link->lldp_file) < 0) { - r = -errno; - goto finish; - } - -finish: - if (r < 0) { - (void) unlink(link->lldp_file); - if (temp_path) - (void) unlink(temp_path); - - log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); - } - - if (l) { - for (i = 0; i < n; i++) - sd_lldp_neighbor_unref(l[i]); - free(l); - } - - return r; -} - -static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { - Link *link = userdata; - int r; - - assert(link); - - (void) link_lldp_save(link); - - if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { - /* If we received information about a new neighbor, restart the LLDP "fast" logic */ - - log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); - - r = link_lldp_emit_start(link); - if (r < 0) - log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); - } -} - static int link_acquire_ipv6_conf(Link *link) { int r; @@ -1894,7 +1579,6 @@ static int link_acquire_ipv4_conf(Link *link) { int r; assert(link); - assert(link->network); assert(link->manager); assert(link->manager->event); @@ -1984,6 +1668,9 @@ static int link_configure_addrgen_mode(Link *link) { assert(link->manager); assert(link->manager->rtnl); + if (!socket_ipv6_is_supported()) + return 0; + log_link_debug(link, "Setting address genmode for link"); r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); @@ -2077,57 +1764,6 @@ static int link_up(Link *link) { return log_link_error_errno(link, r, "Could not set MAC address: %m"); } - if (link_ipv6_enabled(link)) { - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - - /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ - r = sd_netlink_message_open_container(req, AF_INET6); - if (r < 0) - return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m"); - - if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) { - r = sd_netlink_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); - } - - r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -static int link_up_can(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - - log_link_debug(link, "Bringing CAN link up"); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); - if (r < 0) - return log_link_error_errno(link, r, "Could not set link flags: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler, link_netlink_destroy_callback, link); if (r < 0) @@ -2138,119 +1774,6 @@ static int link_up_can(Link *link) { return 0; } -static int link_set_can(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - assert(link); - assert(link->network); - assert(link->manager); - assert(link->manager->rtnl); - - log_link_debug(link, "link_set_can"); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK); - if (r < 0) - return log_link_error_errno(link, r, "Could not set netlink flags: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_link_error_errno(link, r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) { - struct can_bittiming bt = { - .bitrate = link->network->can_bitrate, - .sample_point = link->network->can_sample_point, - }; - - if (link->network->can_bitrate > UINT32_MAX) { - log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate); - return -ERANGE; - } - - log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate); - if (link->network->can_sample_point > 0) - log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); - else - log_link_debug(link, "Using default sample point"); - - r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); - } - - if (link->network->can_restart_us > 0) { - char time_string[FORMAT_TIMESPAN_MAX]; - uint64_t restart_ms; - - if (link->network->can_restart_us == USEC_INFINITY) - restart_ms = 0; - else - restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC); - - format_timespan(time_string, FORMAT_TIMESPAN_MAX, restart_ms * 1000, MSEC_PER_SEC); - - if (restart_ms > UINT32_MAX) { - log_link_error(link, "restart timeout (%s) too big.", time_string); - return -ERANGE; - } - - log_link_debug(link, "Setting restart = %s", time_string); - - r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m"); - } - - if (link->network->can_triple_sampling >= 0) { - struct can_ctrlmode cm = { - .mask = CAN_CTRLMODE_3_SAMPLES, - .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0, - }; - - log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis"); - - r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_link_error_errno(link, r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_link_error_errno(link, r, "Failed to close netlink container: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - if (!(link->flags & IFF_UP)) { - r = link_up_can(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - } - - log_link_debug(link, "link_set_can done"); - - return r; -} - static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -2263,13 +1786,10 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link if (r < 0) log_link_warning_errno(link, r, "Could not bring down interface: %m"); - if (streq_ptr(link->kind, "can")) - link_set_can(link); - return 1; } -int link_down(Link *link) { +int link_down(Link *link, link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -2288,7 +1808,8 @@ int link_down(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not set link flags: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, link_down_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, + callback ?: link_down_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -2320,7 +1841,7 @@ static int link_handle_bound_to_list(Link *link) { } if (!required_up && link_is_up) { - r = link_down(link); + r = link_down(link, NULL); if (r < 0) return r; } else if (required_up && !link_is_up) { @@ -2531,6 +2052,55 @@ static void link_free_carrier_maps(Link *link) { return; } +static int link_append_to_master(Link *link, NetDev *netdev) { + Link *master; + int r; + + assert(link); + assert(netdev); + + r = link_get(link->manager, netdev->ifindex, &master); + if (r < 0) + return r; + + r = set_ensure_allocated(&master->slaves, NULL); + if (r < 0) + return r; + + r = set_put(master->slaves, link); + if (r < 0) + return r; + + link_ref(link); + return 0; +} + +static void link_drop_from_master(Link *link, NetDev *netdev) { + Link *master; + + assert(link); + + if (!link->manager || !netdev) + return; + + if (link_get(link->manager, netdev->ifindex, &master) < 0) + return; + + link_unref(set_remove(master->slaves, link)); +} + +static void link_detach_from_manager(Link *link) { + if (!link || !link->manager) + return; + + link_unref(set_remove(link->manager->links_requesting_uuid, link)); + link_clean(link); + + /* The following must be called at last. */ + assert_se(hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)) == link); + link_unref(link); +} + void link_drop(Link *link) { if (!link || link->state == LINK_STATE_LINGER) return; @@ -2539,15 +2109,15 @@ void link_drop(Link *link) { link_free_carrier_maps(link); + if (link->network) { + link_drop_from_master(link, link->network->bridge); + link_drop_from_master(link, link->network->bond); + } + log_link_debug(link, "Link removed"); (void) unlink(link->state_file); - link_detach_from_manager(link); - - link_unref(link); - - return; } static int link_joined(Link *link) { @@ -2640,7 +2210,7 @@ static int link_enter_join_netdev(Link *link) { assert(link); assert(link->network); - assert(link->state == LINK_STATE_PENDING); + assert(link->state == LINK_STATE_INITIALIZED); link_set_state(link, LINK_STATE_CONFIGURING); @@ -2998,62 +2568,12 @@ static int link_drop_config(Link *link) { return 0; } -static int link_update_lldp(Link *link) { - int r; - - assert(link); - - if (!link->lldp) - return 0; - - if (link->flags & IFF_UP) { - r = sd_lldp_start(link->lldp); - if (r > 0) - log_link_debug(link, "Started LLDP."); - } else { - r = sd_lldp_stop(link->lldp); - if (r > 0) - log_link_debug(link, "Stopped LLDP."); - } - - return r; -} - -static int link_configure_can(Link *link) { - int r; - - if (streq_ptr(link->kind, "can")) { - /* The CAN interface must be down to configure bitrate, etc... */ - if ((link->flags & IFF_UP)) { - r = link_down(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - - return 0; - } - - return link_set_can(link); - } - - if (!(link->flags & IFF_UP)) { - r = link_up_can(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - } - - return 0; -} - static int link_configure(Link *link) { int r; assert(link); assert(link->network); - assert(link->state == LINK_STATE_PENDING); + assert(link->state == LINK_STATE_INITIALIZED); if (STRPTR_IN_SET(link->kind, "can", "vcan")) return link_configure_can(link); @@ -3106,7 +2626,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - if (link_ipv4ll_enabled(link)) { + if (link_ipv4ll_enabled(link) || link_ipv4ll_fallback_enabled(link)) { r = ipv4ll_configure(link); if (r < 0) return r; @@ -3152,47 +2672,18 @@ static int link_configure(Link *link) { } if (link_lldp_rx_enabled(link)) { - r = sd_lldp_new(&link->lldp); - if (r < 0) - return r; - - r = sd_lldp_set_ifindex(link->lldp, link->ifindex); - if (r < 0) - return r; - - r = sd_lldp_match_capabilities(link->lldp, - link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? - SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : - SD_LLDP_SYSTEM_CAPABILITIES_ALL); - if (r < 0) - return r; - - r = sd_lldp_set_filter_address(link->lldp, &link->mac); - if (r < 0) - return r; - - r = sd_lldp_attach_event(link->lldp, NULL, 0); - if (r < 0) - return r; - - r = sd_lldp_set_callback(link->lldp, lldp_handler, link); - if (r < 0) - return r; - - r = link_update_lldp(link); + r = link_lldp_rx_configure(link); if (r < 0) return r; } - r = link_set_mtu(link, link->network->mtu, link->network->mtu_is_set); + r = link_configure_mtu(link); if (r < 0) return r; - if (socket_ipv6_is_supported()) { - r = link_configure_addrgen_mode(link); - if (r < 0) - return r; - } + r = link_configure_addrgen_mode(link); + if (r < 0) + return r; return link_configure_after_setting_mtu(link); } @@ -3202,7 +2693,7 @@ static int link_configure_after_setting_mtu(Link *link) { assert(link); assert(link->network); - assert(link->state == LINK_STATE_PENDING); + assert(link->state == LINK_STATE_INITIALIZED); if (link->setting_mtu) return 0; @@ -3353,10 +2844,13 @@ static int link_initialized_and_synced(Link *link) { assert(link->ifname); assert(link->manager); - if (link->state != LINK_STATE_PENDING) - return 1; + /* We may get called either from the asynchronous netlink callback, + * or directly for link_add() if running in a container. See link_add(). */ + if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) + return 0; log_link_debug(link, "Link state is up-to-date"); + link_set_state(link, LINK_STATE_INITIALIZED); r = link_new_bound_by_list(link); if (r < 0) @@ -3371,7 +2865,7 @@ static int link_initialized_and_synced(Link *link) { &link->mac, &network); if (r == -ENOENT) { link_enter_unmanaged(link); - return 1; + return 0; } else if (r == 0 && network->unmanaged) { link_enter_unmanaged(link); return 0; @@ -3408,7 +2902,7 @@ static int link_initialized_and_synced(Link *link) { if (r < 0) return r; - return 1; + return 0; } static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -3432,6 +2926,7 @@ int link_initialized(Link *link, sd_device *device) { return 0; log_link_debug(link, "udev initialized link"); + link_set_state(link, LINK_STATE_INITIALIZED); link->sd_device = sd_device_ref(device); @@ -3722,7 +3217,7 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { link->ipv6ll_address = *address; link_check_ready(link); - if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { + if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { r = link_acquire_ipv6_conf(link); if (r < 0) { link_enter_failed(link); @@ -3738,7 +3233,7 @@ static int link_carrier_gained(Link *link) { assert(link); - if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { + if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { r = link_acquire_conf(link); if (r < 0) { link_enter_failed(link); @@ -3830,7 +3325,7 @@ int link_update(Link *link, sd_netlink_message *m) { assert(m); if (link->state == LINK_STATE_LINGER) { - log_link_info(link, "Link readded"); + log_link_info(link, "Link re-added"); link_set_state(link, LINK_STATE_CONFIGURING); r = link_new_carrier_maps(link); @@ -4042,12 +3537,11 @@ int link_save(Link *link) { admin_state, oper_state); if (link->network) { - bool space; + char **dhcp6_domains = NULL, **dhcp_domains = NULL; + const char *dhcp_domainname = NULL, *p; sd_dhcp6_lease *dhcp6_lease = NULL; - const char *dhcp_domainname = NULL; - char **dhcp6_domains = NULL; - char **dhcp_domains = NULL; unsigned j; + bool space; fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", yes_no(link->network->required_for_online)); @@ -4164,7 +3658,10 @@ int link_save(Link *link) { (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); } - ordered_set_print(f, "DOMAINS=", link->network->search_domains); + fputs("DOMAINS=", f); + space = false; + ORDERED_SET_FOREACH(p, link->network->search_domains, i) + fputs_with_space(f, p, NULL, &space); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { NDiscDNSSL *dd; @@ -4182,7 +3679,10 @@ int link_save(Link *link) { fputc('\n', f); - ordered_set_print(f, "ROUTE_DOMAINS=", link->network->route_domains); + fputs("ROUTE_DOMAINS=", f); + space = false; + ORDERED_SET_FOREACH(p, link->network->route_domains, i) + fputs_with_space(f, p, NULL, &space); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { NDiscDNSSL *dd; @@ -4349,6 +3849,7 @@ void link_clean(Link *link) { static const char* const link_state_table[_LINK_STATE_MAX] = { [LINK_STATE_PENDING] = "pending", + [LINK_STATE_INITIALIZED] = "initialized", [LINK_STATE_CONFIGURING] = "configuring", [LINK_STATE_CONFIGURED] = "configured", [LINK_STATE_UNMANAGED] = "unmanaged", diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index e65246c87d..cc4cd07075 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -20,12 +20,13 @@ #include "set.h" typedef enum LinkState { - LINK_STATE_PENDING, - LINK_STATE_CONFIGURING, - LINK_STATE_CONFIGURED, - LINK_STATE_UNMANAGED, - LINK_STATE_FAILED, - LINK_STATE_LINGER, + LINK_STATE_PENDING, /* udev has not initialized the link */ + LINK_STATE_INITIALIZED, /* udev has initialized the link */ + LINK_STATE_CONFIGURING, /* configuring addresses, routes, etc. */ + LINK_STATE_CONFIGURED, /* everything is configured */ + LINK_STATE_UNMANAGED, /* Unmanaged=yes is set */ + LINK_STATE_FAILED, /* at least one configuration process failed */ + LINK_STATE_LINGER, /* RTM_DELLINK for the link has been received */ _LINK_STATE_MAX, _LINK_STATE_INVALID = -1 } LinkState; @@ -121,7 +122,7 @@ typedef struct Link { Hashmap *bound_by_links; Hashmap *bound_to_links; - Hashmap *slaves; + Set *slaves; } Link; typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*); @@ -131,13 +132,14 @@ int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *re Link *link_unref(Link *link); Link *link_ref(Link *link); +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref); int link_get(Manager *m, int ifindex, Link **ret); int link_add(Manager *manager, sd_netlink_message *message, Link **ret); void link_drop(Link *link); -int link_down(Link *link); +int link_down(Link *link, link_netlink_message_handler_t callback); void link_enter_failed(Link *link); int link_initialized(Link *link, sd_device *device); @@ -156,9 +158,12 @@ bool link_has_carrier(Link *link); int link_ipv6ll_gained(Link *link, const struct in6_addr *address); -int link_set_mtu(Link *link, uint32_t mtu, bool force); +int link_set_mtu(Link *link, uint32_t mtu); int ipv4ll_configure(Link *link); +bool link_ipv4ll_enabled(Link *link); +bool link_ipv4ll_fallback_enabled(Link *link); + int dhcp4_configure(Link *link); int dhcp4_set_client_identifier(Link *link); int dhcp4_set_promote_secondaries(Link *link); @@ -167,6 +172,8 @@ int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link, int ir); int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link); +int link_stop_clients(Link *link); + const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; @@ -179,6 +186,7 @@ int link_send_changed(Link *link, const char *property, ...) _sentinel_; uint32_t link_get_vrf_table(Link *link); uint32_t link_get_dhcp_route_table(Link *link); uint32_t link_get_ipv6_accept_ra_route_table(Link *link); +int link_request_set_routes(Link *link); #define ADDRESS_FMT_VAL(address) \ be32toh((address).s_addr) >> 24, \ diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c new file mode 100644 index 0000000000..95018d37a3 --- /dev/null +++ b/src/network/networkd-lldp-rx.c @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <net/if.h> +#include <unistd.h> + +#include "fd-util.h" +#include "fileio.h" +#include "networkd-link.h" +#include "networkd-lldp-rx.h" +#include "networkd-lldp-tx.h" +#include "networkd-network.h" +#include "string-table.h" +#include "string-util.h" +#include "tmpfile-util.h" + +DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); + +static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { + [LLDP_MODE_NO] = "no", + [LLDP_MODE_YES] = "yes", + [LLDP_MODE_ROUTERS_ONLY] = "routers-only", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); + +bool link_lldp_rx_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + /* LLDP should be handled on bridge slaves as those have a direct + * connection to their peers not on the bridge master. Linux doesn't + * even (by default) forward lldp packets to the bridge master.*/ + if (streq_ptr("bridge", link->kind)) + return false; + + return link->network->lldp_mode != LLDP_MODE_NO; +} + +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + (void) link_lldp_save(link); + + if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + /* If we received information about a new neighbor, restart the LLDP "fast" logic */ + + log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); + + r = link_lldp_emit_start(link); + if (r < 0) + log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); + } +} + +int link_lldp_rx_configure(Link *link) { + int r; + + r = sd_lldp_new(&link->lldp); + if (r < 0) + return r; + + r = sd_lldp_set_ifindex(link->lldp, link->ifindex); + if (r < 0) + return r; + + r = sd_lldp_match_capabilities(link->lldp, + link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? + SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : + SD_LLDP_SYSTEM_CAPABILITIES_ALL); + if (r < 0) + return r; + + r = sd_lldp_set_filter_address(link->lldp, &link->mac); + if (r < 0) + return r; + + r = sd_lldp_attach_event(link->lldp, NULL, 0); + if (r < 0) + return r; + + r = sd_lldp_set_callback(link->lldp, lldp_handler, link); + if (r < 0) + return r; + + r = link_update_lldp(link); + if (r < 0) + return r; + + return 0; +} + +int link_update_lldp(Link *link) { + int r; + + assert(link); + + if (!link->lldp) + return 0; + + if (link->flags & IFF_UP) { + r = sd_lldp_start(link->lldp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start LLDP: %m"); + if (r > 0) + log_link_debug(link, "Started LLDP."); + } else { + r = sd_lldp_stop(link->lldp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to stop LLDP: %m"); + if (r > 0) + log_link_debug(link, "Stopped LLDP."); + } + + return r; +} + +int link_lldp_save(Link *link) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + sd_lldp_neighbor **l = NULL; + int n = 0, r, i; + + assert(link); + assert(link->lldp_file); + + if (!link->lldp) { + (void) unlink(link->lldp_file); + return 0; + } + + r = sd_lldp_get_neighbors(link->lldp, &l); + if (r < 0) + goto finish; + if (r == 0) { + (void) unlink(link->lldp_file); + goto finish; + } + + n = r; + + r = fopen_temporary(link->lldp_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + for (i = 0; i < n; i++) { + const void *p; + le64_t u; + size_t sz; + + r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); + if (r < 0) + goto finish; + + u = htole64(sz); + (void) fwrite(&u, 1, sizeof(u), f); + (void) fwrite(p, 1, sz, f); + } + + r = fflush_and_check(f); + if (r < 0) + goto finish; + + if (rename(temp_path, link->lldp_file) < 0) { + r = -errno; + goto finish; + } + +finish: + if (r < 0) { + (void) unlink(link->lldp_file); + if (temp_path) + (void) unlink(temp_path); + + log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); + } + + if (l) { + for (i = 0; i < n; i++) + sd_lldp_neighbor_unref(l[i]); + free(l); + } + + return r; +} diff --git a/src/network/networkd-lldp-rx.h b/src/network/networkd-lldp-rx.h new file mode 100644 index 0000000000..12f512f628 --- /dev/null +++ b/src/network/networkd-lldp-rx.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "conf-parser.h" + +typedef struct Link Link; + +typedef enum LLDPMode { + LLDP_MODE_NO = 0, + LLDP_MODE_YES = 1, + LLDP_MODE_ROUTERS_ONLY = 2, + _LLDP_MODE_MAX, + _LLDP_MODE_INVALID = -1, +} LLDPMode; + +bool link_lldp_rx_enabled(Link *link); +int link_lldp_rx_configure(Link *link); +int link_update_lldp(Link *link); +int link_lldp_save(Link *link); + +const char* lldp_mode_to_string(LLDPMode m) _const_; +LLDPMode lldp_mode_from_string(const char *s) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mode); diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 8fd6365e68..00c4358a0a 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -2,6 +2,7 @@ #include <endian.h> #include <inttypes.h> +#include <net/if.h> #include <string.h> #include "alloc-util.h" @@ -9,6 +10,7 @@ #include "fd-util.h" #include "hostname-util.h" #include "missing_network.h" +#include "networkd-link.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "parse-util.h" @@ -38,6 +40,21 @@ static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, }; +bool link_lldp_emit_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + return link->network->lldp_emit != LLDP_EMIT_NO; +} + static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { assert(p); diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index 6842804a7e..561becda41 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -1,8 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <stdbool.h> + #include "conf-parser.h" -#include "networkd-link.h" + +typedef struct Link Link; typedef enum LLDPEmit { LLDP_EMIT_NO, @@ -12,6 +15,7 @@ typedef enum LLDPEmit { _LLDP_EMIT_MAX, } LLDPEmit; +bool link_lldp_emit_enabled(Link *link); int link_lldp_emit_start(Link *link); void link_lldp_emit_stop(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 677f66a478..1af25dc812 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <netinet/in.h> #include <sys/socket.h> +#include <unistd.h> #include <linux/if.h> #include <linux/fib_rules.h> -#include <unistd.h> #include "sd-daemon.h" #include "sd-netlink.h" @@ -266,7 +267,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo unsigned char protocol, scope, tos, table, rt_type; int family; unsigned char dst_prefixlen, src_prefixlen; - union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {}; + union in_addr_union dst = IN_ADDR_NULL, gw = IN_ADDR_NULL, src = IN_ADDR_NULL, prefsrc = IN_ADDR_NULL; Route *route = NULL; int r; @@ -477,19 +478,17 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo } int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { + _cleanup_free_ char *buf = NULL; Manager *m = userdata; Link *link = NULL; uint16_t type; - unsigned char flags; - int family; - unsigned char prefixlen; - unsigned char scope; - union in_addr_union in_addr; + unsigned char flags, prefixlen, scope; + union in_addr_union in_addr = IN_ADDR_NULL; struct ifa_cacheinfo cinfo; Address *address = NULL; - char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX]; + char valid_buf[FORMAT_TIMESPAN_MAX]; const char *valid_str = NULL; - int r, ifindex; + int ifindex, family, r; assert(rtnl); assert(message); @@ -577,8 +576,9 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, assert_not_reached("Received unsupported address family"); } - if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) { - log_link_warning(link, "Could not print address, ignoring"); + r = in_addr_to_string(family, &in_addr, &buf); + if (r < 0) { + log_link_warning_errno(link, r, "Could not print address, ignoring: %m"); return 0; } @@ -586,12 +586,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0 && r != -ENODATA) { log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); return 0; - } else if (r >= 0) { - if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, - cinfo.ifa_valid * USEC_PER_SEC, - USEC_PER_SEC); - } + } else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) + valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, + cinfo.ifa_valid * USEC_PER_SEC, + USEC_PER_SEC); (void) address_get(link, family, &in_addr, prefixlen, &address); @@ -728,7 +726,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0; struct fib_rule_port_range sport = {}, dport = {}; - union in_addr_union to = {}, from = {}; + union in_addr_union to = IN_ADDR_NULL, from = IN_ADDR_NULL; RoutingPolicyRule *rule = NULL; uint32_t fwmark = 0, table = 0; const char *iif = NULL, *oif = NULL; @@ -1279,7 +1277,9 @@ static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in DEFINE_PRIVATE_HASH_OPS(dhcp6_prefixes_hash_ops, struct in6_addr, dhcp6_prefixes_hash_func, dhcp6_prefixes_compare_func); int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { + _cleanup_free_ struct in6_addr *a = NULL; _cleanup_free_ char *buf = NULL; + Link *assigned_link; Route *route; int r; @@ -1298,11 +1298,27 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf); log_link_debug(link, "Adding prefix route %s/64", strnull(buf)); + assigned_link = hashmap_get(m->dhcp6_prefixes, addr); + if (assigned_link) { + assert(assigned_link == link); + return 0; + } + + a = newdup(struct in6_addr, addr, 1); + if (!a) + return -ENOMEM; + r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops); if (r < 0) return r; - return hashmap_put(m->dhcp6_prefixes, addr, link); + r = hashmap_put(m->dhcp6_prefixes, a, link); + if (r < 0) + return r; + + TAKE_PTR(a); + link_ref(link); + return 0; } static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { @@ -1318,21 +1334,21 @@ static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Lin } static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) { + _cleanup_free_ struct in6_addr *a = NULL; + _cleanup_(link_unrefp) Link *l = NULL; _cleanup_free_ char *buf = NULL; Route *route; - Link *l; int r; assert_return(m, -EINVAL); assert_return(addr, -EINVAL); - l = hashmap_remove(m->dhcp6_prefixes, addr); + l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a); if (!l) return -EINVAL; (void) sd_radv_remove_prefix(l->radv, addr, 64); - r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, - 0, 0, 0, &route); + r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, 0, 0, 0, &route); if (r < 0) return r; @@ -1354,12 +1370,9 @@ int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) { assert_return(m, -EINVAL); assert_return(link, -EINVAL); - HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) { - if (l != link) - continue; - - (void) manager_dhcp6_prefix_remove(m, addr); - } + HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) + if (l == link) + (void) manager_dhcp6_prefix_remove(m, addr); return 0; } @@ -1402,8 +1415,6 @@ int manager_new(Manager **ret) { if (r < 0) return r; - LIST_HEAD_INIT(m->networks); - r = sd_resolve_default(&m->resolve); if (r < 0) return r; @@ -1426,8 +1437,8 @@ int manager_new(Manager **ret) { } void manager_free(Manager *m) { + struct in6_addr *a; AddressPool *pool; - Network *network; Link *link; if (!m) @@ -1435,29 +1446,25 @@ void manager_free(Manager *m) { free(m->state_file); - sd_netlink_unref(m->rtnl); - sd_netlink_unref(m->genl); - sd_resolve_unref(m->resolve); - - while ((link = hashmap_first(m->dhcp6_prefixes))) - manager_dhcp6_prefix_remove_all(m, link); + while ((a = hashmap_first_key(m->dhcp6_prefixes))) + (void) manager_dhcp6_prefix_remove(m, a); hashmap_free(m->dhcp6_prefixes); while ((link = hashmap_steal_first(m->links))) { if (link->dhcp6_client) (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link); + + link_stop_clients(link); + link_unref(link); } m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); - m->links = hashmap_free(m->links); - m->links_requesting_uuid = set_free(m->links_requesting_uuid); - set_free(m->duids_requesting_uuid); - - while ((network = m->networks)) - network_free(network); + m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref); + m->links = hashmap_free_with_destructor(m->links, link_unref); - hashmap_free(m->networks_by_name); + m->duids_requesting_uuid = set_free(m->duids_requesting_uuid); + m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref); m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); @@ -1470,6 +1477,10 @@ void manager_free(Manager *m) { m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free); set_free_with_destructor(m->rules_saved, routing_policy_rule_free); + sd_netlink_unref(m->rtnl); + sd_netlink_unref(m->genl); + sd_resolve_unref(m->resolve); + sd_event_unref(m->event); sd_device_monitor_unref(m->device_monitor); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 35ab6bedb1..06fa9d8d32 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include <arpa/inet.h> - #include "sd-bus.h" #include "sd-device.h" #include "sd-event.h" @@ -37,9 +35,8 @@ struct Manager { Hashmap *links; Hashmap *netdevs; - Hashmap *networks_by_name; + OrderedHashmap *networks; Hashmap *dhcp6_prefixes; - LIST_HEAD(Network, networks); LIST_HEAD(AddressPool, address_pools); usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index eb470a4d48..28d46f5fe3 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -39,7 +39,7 @@ static int ndisc_netlink_message_handler(sd_netlink *rtnl, sd_netlink_message *m static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; - struct in6_addr gateway; + union in_addr_union gateway; uint16_t lifetime; unsigned preference; uint32_t mtu; @@ -58,31 +58,35 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (lifetime == 0) /* not a default router */ return 0; - r = sd_ndisc_router_get_address(rt, &gateway); + r = sd_ndisc_router_get_address(rt, &gateway.in6); if (r < 0) return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - SET_FOREACH(address, link->addresses, i) - if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) { - char buffer[INET6_ADDRSTRLEN]; + SET_FOREACH(address, link->addresses, i) { + if (address->family != AF_INET6) + continue; + if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) { + _cleanup_free_ char *buffer = NULL; + (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer); log_link_debug(link, "No NDisc route added, gateway %s matches local address", - inet_ntop(AF_INET6, - &address->in_addr.in6, - buffer, sizeof(buffer))); + strnull(buffer)); return 0; } + } - SET_FOREACH(address, link->addresses_foreign, i) - if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) { - char buffer[INET6_ADDRSTRLEN]; + SET_FOREACH(address, link->addresses_foreign, i) { + if (address->family != AF_INET6) + continue; + if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) { + _cleanup_free_ char *buffer = NULL; + (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer); log_link_debug(link, "No NDisc route added, gateway %s matches local address", - inet_ntop(AF_INET6, - &address->in_addr.in6, - buffer, sizeof(buffer))); + strnull(buffer)); return 0; } + } r = sd_ndisc_router_get_preference(rt, &preference); if (r < 0) @@ -107,7 +111,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->priority = link->network->dhcp_route_metric; route->protocol = RTPROT_RA; route->pref = preference; - route->gw.in6 = gateway; + route->gw = gateway; route->lifetime = time_now + lifetime * USEC_PER_SEC; route->mtu = mtu; @@ -555,7 +559,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { uint64_t flags; - int r = 0; + int r; assert(link); assert(link->network); diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c index 07e453cc80..e3ba148ce1 100644 --- a/src/network/networkd-network-bus.c +++ b/src/network/networkd-network-bus.c @@ -87,6 +87,7 @@ int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; Network *network; + Iterator i; int r; assert(bus); @@ -94,7 +95,7 @@ int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char assert(m); assert(nodes); - LIST_FOREACH(networks, network, m->networks) { + ORDERED_HASHMAP_FOREACH(network, m->networks, i) { char *p; p = network_bus_path(network); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 0db59473ff..825fbf603a 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -44,6 +44,7 @@ Network.VLAN, config_parse_stacked_netdev, Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names) +Network.IPVTAP, config_parse_stacked_netdev, NETDEV_KIND_IPVTAP, offsetof(Network, stacked_netdev_names) Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names) Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names) Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names) @@ -51,8 +52,9 @@ Network.Tunnel, config_parse_stacked_netdev, Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name) Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) -Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) +Network.LinkLocalAddressing, config_parse_link_local_address_family_boolean, 0, offsetof(Network, link_local) Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) +Network.DefaultRouteOnDevice, config_parse_bool, 0, offsetof(Network, default_route_on_device) Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) @@ -127,6 +129,7 @@ Route.Type, config_parse_route_type, Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0 Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0 Route.QuickAck, config_parse_quickack, 0, 0 +Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) @@ -140,6 +143,7 @@ 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.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0 DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) @@ -148,7 +152,9 @@ DHCP.RouteTable, config_parse_section_route_table, DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, 0 DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) +DHCP.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) +DHCP.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) @@ -176,9 +182,15 @@ Bridge.MulticastFlood, config_parse_tristate, Bridge.MulticastToUnicast, config_parse_tristate, 0, offsetof(Network, multicast_to_unicast) Bridge.NeighborSuppression, config_parse_tristate, 0, offsetof(Network, neighbor_suppression) Bridge.Learning, config_parse_tristate, 0, offsetof(Network, learning) +Bridge.ProxyARP, config_parse_tristate, 0, offsetof(Network, bridge_proxy_arp) +Bridge.ProxyARPWiFi, config_parse_tristate, 0, offsetof(Network, bridge_proxy_arp_wifi) Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority) +Bridge.MulticastRouter, config_parse_multicast_router, 0, offsetof(Network, multicast_router) BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 +BridgeFDB.Destination, config_parse_fdb_destination, 0, 0 +BridgeFDB.VNI, config_parse_fdb_vxlan_vni, 0, 0 +BridgeFDB.AssociatedWith, config_parse_fdb_ntf_flags, 0, 0 BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 52cfc4bec1..fcfb6d2e17 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <net/if.h> +#include <netinet/in.h> #include <linux/netdevice.h> #include "alloc-util.h" @@ -141,24 +143,6 @@ static int network_resolve_stacked_netdevs(Network *network) { return 0; } -static uint32_t network_get_stacked_netdevs_mtu(Network *network) { - uint32_t mtu = 0; - NetDev *dev; - Iterator i; - - HASHMAP_FOREACH(dev, network->stacked_netdevs, i) - if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0) - /* See vlan_dev_change_mtu() in kernel. - * Note that the additional 4bytes may not be necessary for all devices. */ - mtu = MAX(mtu, dev->mtu + 4); - - else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu) - /* See macvlan_change_mtu() in kernel. */ - mtu = dev->mtu; - - return mtu; -} - int network_verify(Network *network) { Address *address, *address_next; Route *route, *route_next; @@ -167,11 +151,18 @@ int network_verify(Network *network) { AddressLabel *label, *label_next; Prefix *prefix, *prefix_next; RoutingPolicyRule *rule, *rule_next; - uint32_t mtu; assert(network); assert(network->filename); + if (set_isempty(network->match_mac) && strv_isempty(network->match_path) && + strv_isempty(network->match_driver) && strv_isempty(network->match_type) && + strv_isempty(network->match_name) && !network->conditions) + log_warning("%s: No valid settings found in the [Match] section. " + "The file will match all interfaces. " + "If that is intended, please add Name=* in the [Match] section.", + network->filename); + /* skip out early if configuration does not match the environment */ if (!condition_test_list(network->conditions, NULL, NULL, NULL)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), @@ -228,6 +219,13 @@ int network_verify(Network *network) { if (network->link_local < 0) network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6; + if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) && + !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) { + log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. " + "Disabling the fallback assignment.", network->filename); + SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false); + } + if (network->ipv6_accept_ra < 0 && network->bridge) network->ipv6_accept_ra = false; @@ -235,16 +233,7 @@ int network_verify(Network *network) { if (network->ip_masquerade) network->ip_forward |= ADDRESS_FAMILY_IPV4; - network->mtu_is_set = network->mtu > 0; - mtu = network_get_stacked_netdevs_mtu(network); - if (network->mtu < mtu) { - if (network->mtu_is_set) - log_notice("%s: Bumping MTUBytes= from %"PRIu32" to %"PRIu32" because of stacked device", - network->filename, network->mtu, mtu); - network->mtu = mtu; - } - - if (network->mtu_is_set && network->dhcp_use_mtu) { + if (network->mtu > 0 && network->dhcp_use_mtu) { log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. " "Disabling UseMTU=.", network->filename); network->dhcp_use_mtu = false; @@ -283,7 +272,7 @@ int network_verify(Network *network) { int network_load_one(Manager *manager, const char *filename) { _cleanup_free_ char *fname = NULL, *name = NULL; - _cleanup_(network_freep) Network *network = NULL; + _cleanup_(network_unrefp) Network *network = NULL; _cleanup_fclose_ FILE *file = NULL; const char *dropin_dirname; char *d; @@ -329,6 +318,9 @@ int network_load_one(Manager *manager, const char *filename) { .filename = TAKE_PTR(fname), .name = TAKE_PTR(name), + .manager = manager, + .n_ref = 1, + .required_for_online = true, .required_operstate_for_online = LINK_OPERSTATE_DEGRADED, .dhcp = ADDRESS_FAMILY_NO, @@ -336,12 +328,12 @@ int network_load_one(Manager *manager, const char *filename) { .dhcp_use_dns = true, .dhcp_use_hostname = true, .dhcp_use_routes = true, - /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */ + /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */ .dhcp_send_hostname = true, /* To enable/disable RFC7844 Anonymity Profiles */ .dhcp_anonymize = false, .dhcp_route_metric = DHCP_ROUTE_METRIC, - /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */ + /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */ .dhcp_client_identifier = DHCP_CLIENT_ID_DUID, .dhcp_route_table = RT_TABLE_MAIN, .dhcp_route_table_set = false, @@ -368,7 +360,10 @@ int network_load_one(Manager *manager, const char *filename) { .multicast_to_unicast = -1, .neighbor_suppression = -1, .learning = -1, + .bridge_proxy_arp = -1, + .bridge_proxy_arp_wifi = -1, .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID, + .multicast_router = _MULTICAST_ROUTER_INVALID, .lldp_mode = LLDP_MODE_ROUTERS_ONLY, @@ -431,14 +426,16 @@ int network_load_one(Manager *manager, const char *filename) { if (r < 0) log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename); - LIST_PREPEND(networks, manager->networks, network); - network->manager = manager; + r = network_add_default_route_on_device(network); + if (r < 0) + log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m", + network->filename); - r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops); if (r < 0) return r; - r = hashmap_put(manager->networks_by_name, network->name, network); + r = ordered_hashmap_put(manager->networks, network->name, network); if (r < 0) return r; @@ -450,21 +447,19 @@ int network_load_one(Manager *manager, const char *filename) { } int network_load(Manager *manager) { - Network *network; _cleanup_strv_free_ char **files = NULL; char **f; int r; assert(manager); - while ((network = manager->networks)) - network_free(network); + ordered_hashmap_clear_with_destructor(manager->networks, network_unref); r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS); if (r < 0) return log_error_errno(r, "Failed to enumerate network files: %m"); - STRV_FOREACH_BACKWARDS(f, files) { + STRV_FOREACH(f, files) { r = network_load_one(manager, *f); if (r < 0) return r; @@ -473,7 +468,7 @@ int network_load(Manager *manager) { return 0; } -void network_free(Network *network) { +static Network *network_free(Network *network) { IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; RoutingPolicyRule *rule; FdbEntry *fdb_entry; @@ -484,7 +479,7 @@ void network_free(Network *network) { Route *route; if (!network) - return; + return NULL; free(network->filename); @@ -499,7 +494,7 @@ void network_free(Network *network) { free(network->dhcp_vendor_class_identifier); strv_free(network->dhcp_user_class); free(network->dhcp_hostname); - + set_free(network->dhcp_black_listed_ip); free(network->mac); strv_free(network->ntp); @@ -553,11 +548,8 @@ void network_free(Network *network) { hashmap_free(network->rules_by_section); if (network->manager) { - if (network->manager->networks) - LIST_REMOVE(networks, network->manager->networks, network); - - if (network->manager->networks_by_name && network->name) - hashmap_remove(network->manager->networks_by_name, network->name); + if (network->manager->networks && network->name) + ordered_hashmap_remove(network->manager->networks, network->name); if (network->manager->duids_requesting_uuid) set_remove(network->manager->duids_requesting_uuid, &network->duid); @@ -571,9 +563,11 @@ void network_free(Network *network) { set_free_free(network->dnssec_negative_trust_anchors); - free(network); + return mfree(network); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free); + int network_get_by_name(Manager *manager, const char *name, Network **ret) { Network *network; @@ -581,7 +575,7 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) { assert(name); assert(ret); - network = hashmap_get(manager->networks_by_name, name); + network = ordered_hashmap_get(manager->networks, name); if (!network) return -ENOENT; @@ -595,6 +589,7 @@ int network_get(Manager *manager, sd_device *device, Network **ret) { const char *path = NULL, *driver = NULL, *devtype = NULL; Network *network; + Iterator i; assert(manager); assert(ret); @@ -607,7 +602,7 @@ int network_get(Manager *manager, sd_device *device, (void) sd_device_get_devtype(device, &devtype); } - LIST_FOREACH(networks, network, manager->networks) { + ORDERED_HASHMAP_FOREACH(network, manager->networks, i) if (net_match_config(network->match_mac, network->match_path, network->match_driver, network->match_type, network->match_name, @@ -630,7 +625,6 @@ int network_get(Manager *manager, sd_device *device, *ret = network; return 0; } - } *ret = NULL; @@ -641,7 +635,7 @@ int network_apply(Network *network, Link *link) { assert(network); assert(link); - link->network = network; + link->network = network_ref(network); if (network->n_dns > 0 || !strv_isempty(network->ntp) || @@ -686,8 +680,8 @@ int config_parse_stacked_netdev(const char *unit, assert(data); assert(IN_SET(kind, NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP, - NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, NETDEV_KIND_L2TP, - NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL)); + NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN, + NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL)); if (!ifname_valid(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, 0, @@ -810,6 +804,7 @@ int config_parse_ipv4ll( void *userdata) { AddressFamilyBoolean *link_local = data; + int r; assert(filename); assert(lvalue); @@ -820,7 +815,20 @@ int config_parse_ipv4ll( * config_parse_address_family_boolean(), except that it * applies only to IPv4 */ - SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue)); + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse %s=%s, ignoring assignment. " + "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.", + lvalue, rvalue, lvalue); + return 0; + } + + SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r); + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.", + lvalue, rvalue, address_family_boolean_to_string(*link_local)); return 0; } @@ -852,7 +860,7 @@ int config_parse_dhcp( if (s < 0) { /* Previously, we had a slightly different enum here, - * support its values for compatbility. */ + * support its values for compatibility. */ if (streq(rvalue, "none")) s = ADDRESS_FAMILY_NO; @@ -1089,7 +1097,8 @@ int config_parse_dhcp_server_dns( for (;;) { _cleanup_free_ char *w = NULL; - struct in_addr a, *m; + union in_addr_union a; + struct in_addr *m; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) @@ -1102,9 +1111,10 @@ int config_parse_dhcp_server_dns( if (r == 0) break; - if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Failed to parse DNS server address, ignoring: %s", w); + r = in_addr_from_string(AF_INET, w, &a); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DNS server address '%s', ignoring assignment: %m", w); continue; } @@ -1112,7 +1122,7 @@ int config_parse_dhcp_server_dns( if (!m) return log_oom(); - m[n->n_dhcp_server_dns++] = a; + m[n->n_dhcp_server_dns++] = a.in; n->dhcp_server_dns = m; } @@ -1249,7 +1259,8 @@ int config_parse_dhcp_server_ntp( for (;;) { _cleanup_free_ char *w = NULL; - struct in_addr a, *m; + union in_addr_union a; + struct in_addr *m; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) @@ -1262,9 +1273,10 @@ int config_parse_dhcp_server_ntp( if (r == 0) return 0; - if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Failed to parse NTP server address, ignoring: %s", w); + r = in_addr_from_string(AF_INET, w, &a); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse NTP server address '%s', ignoring: %m", w); continue; } @@ -1272,7 +1284,7 @@ int config_parse_dhcp_server_ntp( if (!m) return log_oom(); - m[n->n_dhcp_server_ntp++] = a; + m[n->n_dhcp_server_ntp++] = a.in; n->dhcp_server_ntp = m; } } @@ -1546,6 +1558,119 @@ int config_parse_section_route_table( return 0; } +int config_parse_dhcp_max_attempts( + 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) { + + Network *network = data; + uint64_t a; + int r; + + assert(network); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + network->dhcp_max_attempts = 0; + return 0; + } + + if (streq(rvalue, "infinity")) { + network->dhcp_max_attempts = (uint64_t) -1; + return 0; + } + + r = safe_atou64(rvalue, &a); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue); + return 0; + } + + if (a == 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue); + return 0; + } + + network->dhcp_max_attempts = a; + + return 0; +} + +int config_parse_dhcp_black_listed_ip_address( + 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) { + + Network *network = data; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip); + return 0; + } + + for (p = rvalue;;) { + _cleanup_free_ char *n = NULL; + union in_addr_union ip; + + r = extract_first_word(&p, &n, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCP black listed ip address, ignoring assignment: %s", + rvalue); + return 0; + } + if (r == 0) + return 0; + + r = in_addr_from_string(AF_INET, n, &ip); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "DHCP black listed ip address is invalid, ignoring assignment: %s", n); + continue; + } + + r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL); + if (r < 0) + return log_oom(); + + r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr)); + if (r == -EEXIST) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "DHCP black listed ip address is duplicated, ignoring assignment: %s", n); + continue; + } + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n); + } + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); @@ -1557,16 +1682,6 @@ static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); -DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); - -static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { - [LLDP_MODE_NO] = "no", - [LLDP_MODE_YES] = "yes", - [LLDP_MODE_ROUTERS_ONLY] = "routers-only", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); - int config_parse_iaid(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 852144da3c..7c434f6af2 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -8,12 +8,14 @@ #include "conf-parser.h" #include "dhcp-identifier.h" #include "hashmap.h" +#include "netdev/bridge.h" #include "netdev/netdev.h" #include "networkd-address-label.h" #include "networkd-address.h" #include "networkd-brvlan.h" #include "networkd-fdb.h" #include "networkd-ipv6-proxy-ndp.h" +#include "networkd-lldp-rx.h" #include "networkd-lldp-tx.h" #include "networkd-neighbor.h" #include "networkd-radv.h" @@ -58,14 +60,6 @@ typedef enum DHCPUseDomains { _DHCP_USE_DOMAINS_INVALID = -1, } DHCPUseDomains; -typedef enum LLDPMode { - LLDP_MODE_NO = 0, - LLDP_MODE_YES = 1, - LLDP_MODE_ROUTERS_ONLY = 2, - _LLDP_MODE_MAX, - _LLDP_MODE_INVALID = -1, -} LLDPMode; - typedef struct DUID { /* Value of Type in [DHCP] section */ DUIDType type; @@ -92,6 +86,8 @@ struct Network { char *filename; char *name; + unsigned n_ref; + Set *match_mac; char **match_path; char **match_driver; @@ -116,6 +112,7 @@ struct Network { char *dhcp_vendor_class_identifier; char **dhcp_user_class; char *dhcp_hostname; + uint64_t dhcp_max_attempts; unsigned dhcp_route_metric; uint32_t dhcp_route_table; uint16_t dhcp_client_port; @@ -131,7 +128,9 @@ struct Network { bool rapid_commit; bool dhcp_use_hostname; bool dhcp_route_table_set; + bool dhcp_send_release; DHCPUseDomains dhcp_use_domains; + Set *dhcp_black_listed_ip; /* DHCP Server Support */ bool dhcp_server; @@ -152,6 +151,8 @@ struct Network { AddressFamilyBoolean link_local; bool ipv4ll_route; + bool default_route_on_device; + /* IPv6 prefix delegation support */ RADVPrefixDelegation router_prefix_delegation; usec_t router_lifetime_usec; @@ -178,8 +179,11 @@ struct Network { int multicast_to_unicast; int neighbor_suppression; int learning; + int bridge_proxy_arp; + int bridge_proxy_arp_wifi; uint32_t cost; uint16_t priority; + MulticastRouter multicast_router; bool use_br_vlan; uint16_t pvid; @@ -216,7 +220,6 @@ struct Network { struct ether_addr *mac; uint32_t mtu; - bool mtu_is_set; /* Indicate MTUBytes= is specified. */ int arp; int multicast; int allmulticast; @@ -274,13 +277,11 @@ struct Network { char **ntp; char **bind_carrier; - - LIST_FIELDS(Network, networks); }; -void network_free(Network *network); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); +Network *network_ref(Network *network); +Network *network_unref(Network *network); +DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_unref); int network_load(Manager *manager); int network_load_one(Manager *manager, const char *filename); @@ -304,17 +305,18 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions); CONFIG_PARSER_PROTOTYPE(config_parse_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_timezone); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address); CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns); CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains); -CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mode); CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class); CONFIG_PARSER_PROTOTYPE(config_parse_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts); /* Legacy IPv4LL support */ CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); @@ -331,8 +333,5 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; -const char* lldp_mode_to_string(LLDPMode m) _const_; -LLDPMode lldp_mode_from_string(const char *s) _pure_; - const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 8cb14b588c..fdbf7cac62 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -356,7 +356,7 @@ static int radv_set_dns(Link *link, Link *uplink) { if (link->network->router_dns) { dns = newdup(struct in6_addr, link->network->router_dns, link->network->n_router_dns); - if (dns == NULL) + if (!dns) return -ENOMEM; n_dns = link->network->n_router_dns; @@ -372,7 +372,7 @@ static int radv_set_dns(Link *link, Link *uplink) { goto set_dns; if (uplink) { - if (uplink->network == NULL) { + if (!uplink->network) { log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); return 0; } @@ -411,7 +411,7 @@ static int radv_set_domains(Link *link, Link *uplink) { goto set_domains; if (uplink) { - if (uplink->network == NULL) { + if (!uplink->network) { log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); return 0; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 379077cbfd..24ef5b46bc 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -59,6 +59,7 @@ int route_new(Route **ret) { .table = RT_TABLE_MAIN, .lifetime = USEC_INFINITY, .quickack = -1, + .fast_open_no_cookie = -1, .gateway_onlink = -1, }; @@ -638,12 +639,18 @@ int route_configure( return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m"); } - if (route->quickack != -1) { + if (route->quickack >= 0) { r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack); if (r < 0) return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m"); } + if (route->fast_open_no_cookie >= 0) { + r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie); + if (r < 0) + return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m"); @@ -705,6 +712,30 @@ int network_add_ipv4ll_route(Network *network) { return 0; } +int network_add_default_route_on_device(Network *network) { + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + int r; + + assert(network); + + if (!network->default_route_on_device) + return 0; + + /* DefaultRouteOnDevice= is in [Network] section. */ + r = route_new_static(network, NULL, 0, &n); + if (r < 0) + return r; + + r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst); + if (r < 0) + return r; + + n->family = AF_INET; + + TAKE_PTR(n); + return 0; +} + int config_parse_gateway( const char *unit, const char *filename, @@ -1197,6 +1228,44 @@ int config_parse_quickack( return 0; } +int config_parse_fast_open_no_cookie( + 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) { + + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + Network *network = userdata; + int k, r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, + "Failed to parse TCP fastopen no cookie, ignoring: %s", rvalue); + return 0; + } + + n->fast_open_no_cookie = k; + TAKE_PTR(n); + return 0; +} + int config_parse_route_mtu( const char *unit, const char *filename, diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 1e8320fdc0..4643f77854 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -17,6 +17,7 @@ struct Route { int family; int quickack; + int fast_open_no_cookie; unsigned char dst_prefixlen; unsigned char src_prefixlen; @@ -61,6 +62,7 @@ int route_section_verify(Route *route, Network *network); DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); int network_add_ipv4ll_route(Network *network); +int network_add_default_route_on_device(Network *network); CONFIG_PARSER_PROTOTYPE(config_parse_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src); @@ -74,4 +76,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_protocol); CONFIG_PARSER_PROTOTYPE(config_parse_route_type); CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window); CONFIG_PARSER_PROTOTYPE(config_parse_quickack); +CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie); CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu); diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 28699ba236..4ee0b5489e 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -2,6 +2,7 @@ #pragma once #include <inttypes.h> +#include <netinet/in.h> #include <linux/fib_rules.h> #include <stdbool.h> diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index a392aadd4c..d01de53f7b 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -8,38 +8,26 @@ #include "string-util.h" #include "util.h" -const char *address_family_boolean_to_string(AddressFamilyBoolean b) { - if (IN_SET(b, ADDRESS_FAMILY_YES, ADDRESS_FAMILY_NO)) - return yes_no(b == ADDRESS_FAMILY_YES); - - if (b == ADDRESS_FAMILY_IPV4) - return "ipv4"; - if (b == ADDRESS_FAMILY_IPV6) - return "ipv6"; - - return NULL; -} - -AddressFamilyBoolean address_family_boolean_from_string(const char *s) { - int r; - - /* Make this a true superset of a boolean */ - - r = parse_boolean(s); - if (r > 0) - return ADDRESS_FAMILY_YES; - if (r == 0) - return ADDRESS_FAMILY_NO; - - if (streq(s, "ipv4")) - return ADDRESS_FAMILY_IPV4; - if (streq(s, "ipv6")) - return ADDRESS_FAMILY_IPV6; - - return _ADDRESS_FAMILY_BOOLEAN_INVALID; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option"); +static const char * const address_family_boolean_table[_ADDRESS_FAMILY_BOOLEAN_MAX] = { + [ADDRESS_FAMILY_NO] = "no", + [ADDRESS_FAMILY_YES] = "yes", + [ADDRESS_FAMILY_IPV4] = "ipv4", + [ADDRESS_FAMILY_IPV6] = "ipv6", +}; + +static const char * const link_local_address_family_boolean_table[_ADDRESS_FAMILY_BOOLEAN_MAX] = { + [ADDRESS_FAMILY_NO] = "no", + [ADDRESS_FAMILY_YES] = "yes", + [ADDRESS_FAMILY_IPV4] = "ipv4", + [ADDRESS_FAMILY_IPV6] = "ipv6", + [ADDRESS_FAMILY_FALLBACK] = "fallback", + [ADDRESS_FAMILY_FALLBACK_IPV4] = "ipv4-fallback", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family_boolean, AddressFamilyBoolean, ADDRESS_FAMILY_YES); +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family_boolean, AddressFamilyBoolean, ADDRESS_FAMILY_YES); +DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family_boolean, link_local_address_family_boolean, + AddressFamilyBoolean, "Failed to parse option"); int config_parse_address_family_boolean_with_kernel( const char* unit, @@ -62,7 +50,7 @@ int config_parse_address_family_boolean_with_kernel( /* This function is mostly obsolete now. It simply redirects * "kernel" to "no". In older networkd versions we used to - * distuingish IPForward=off from IPForward=kernel, where the + * distinguish IPForward=off from IPForward=kernel, where the * former would explicitly turn off forwarding while the * latter would simply not touch the setting. But that logic * is gone, hence silently accept the old setting, but turn it @@ -84,7 +72,7 @@ int config_parse_address_family_boolean_with_kernel( } /* Router lifetime can be set with netlink interface since kernel >= 4.5 - * so for the supported kernel we dont need to expire routes in userspace */ + * so for the supported kernel we don't need to expire routes in userspace */ int kernel_route_expiration_supported(void) { static int cached = -1; int r; diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index a49e289351..aafbac7df6 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -7,10 +7,12 @@ typedef enum AddressFamilyBoolean { /* This is a bitmask, though it usually doesn't feel that way! */ - ADDRESS_FAMILY_NO = 0, - ADDRESS_FAMILY_IPV4 = 1 << 0, - ADDRESS_FAMILY_IPV6 = 1 << 1, - ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6, + ADDRESS_FAMILY_NO = 0, + ADDRESS_FAMILY_IPV4 = 1 << 0, + ADDRESS_FAMILY_IPV6 = 1 << 1, + ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6, + ADDRESS_FAMILY_FALLBACK_IPV4 = 1 << 2, + ADDRESS_FAMILY_FALLBACK = ADDRESS_FAMILY_FALLBACK_IPV4 | ADDRESS_FAMILY_IPV6, _ADDRESS_FAMILY_BOOLEAN_MAX, _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, } AddressFamilyBoolean; @@ -21,11 +23,14 @@ typedef struct NetworkConfigSection { char filename[]; } NetworkConfigSection; -CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean); +CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family_boolean); CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean_with_kernel); const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; -AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; +AddressFamilyBoolean address_family_boolean_from_string(const char *s) _pure_; + +const char *link_local_address_family_boolean_to_string(AddressFamilyBoolean b) _const_; +AddressFamilyBoolean link_local_address_family_boolean_from_string(const char *s) _pure_; int kernel_route_expiration_supported(void); @@ -35,7 +40,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free); extern const struct hash_ops network_config_hash_ops; static inline bool section_is_invalid(NetworkConfigSection *section) { - /* If this retuns false, then it does _not_ mean the section is valid. */ + /* If this returns false, then it does _not_ mean the section is valid. */ if (!section) return false; diff --git a/src/network/networkd.c b/src/network/networkd.c index 4b04aa0463..090f3777a5 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <netinet/in.h> #include <sys/stat.h> #include <sys/types.h> diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index 6b110b7110..711954e4a2 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -14,7 +14,6 @@ #include "test-tables.h" int main(int argc, char **argv) { - test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); test_table(bond_ad_select, NETDEV_BOND_AD_SELECT); test_table(bond_arp_all_targets, NETDEV_BOND_ARP_ALL_TARGETS); test_table(bond_arp_validate, NETDEV_BOND_ARP_VALIDATE); @@ -42,6 +41,7 @@ int main(int argc, char **argv) { test_table_sparse(ipvlan_mode, NETDEV_IPVLAN_MODE); test_table_sparse(macvlan_mode, NETDEV_MACVLAN_MODE); + test_table_sparse(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); return EXIT_SUCCESS; } diff --git a/src/network/test-network.c b/src/network/test-network.c index 6e169e0fca..23fcea666e 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <arpa/inet.h> #include <sys/param.h> #include "sd-device.h" @@ -15,32 +16,31 @@ static void test_deserialize_in_addr(void) { _cleanup_free_ struct in_addr *addresses = NULL; _cleanup_free_ struct in6_addr *addresses6 = NULL; - struct in_addr a, b, c; - struct in6_addr d, e, f; + union in_addr_union a, b, c, d, e, f; int size; const char *addresses_string = "192.168.0.1 0:0:0:0:0:FFFF:204.152.189.116 192.168.0.2 ::1 192.168.0.3 1:0:0:0:0:0:0:8"; - assert_se(inet_pton(AF_INET, "0:0:0:0:0:FFFF:204.152.189.116", &a) == 0); - assert_se(inet_pton(AF_INET6, "192.168.0.1", &d) == 0); + assert_se(in_addr_from_string(AF_INET, "0:0:0:0:0:FFFF:204.152.189.116", &a) < 0); + assert_se(in_addr_from_string(AF_INET6, "192.168.0.1", &d) < 0); - assert_se(inet_pton(AF_INET, "192.168.0.1", &a) == 1); - assert_se(inet_pton(AF_INET, "192.168.0.2", &b) == 1); - assert_se(inet_pton(AF_INET, "192.168.0.3", &c) == 1); - assert_se(inet_pton(AF_INET6, "0:0:0:0:0:FFFF:204.152.189.116", &d) == 1); - assert_se(inet_pton(AF_INET6, "::1", &e) == 1); - assert_se(inet_pton(AF_INET6, "1:0:0:0:0:0:0:8", &f) == 1); + assert_se(in_addr_from_string(AF_INET, "192.168.0.1", &a) >= 0); + assert_se(in_addr_from_string(AF_INET, "192.168.0.2", &b) >= 0); + assert_se(in_addr_from_string(AF_INET, "192.168.0.3", &c) >= 0); + assert_se(in_addr_from_string(AF_INET6, "0:0:0:0:0:FFFF:204.152.189.116", &d) >= 0); + assert_se(in_addr_from_string(AF_INET6, "::1", &e) >= 0); + assert_se(in_addr_from_string(AF_INET6, "1:0:0:0:0:0:0:8", &f) >= 0); assert_se((size = deserialize_in_addrs(&addresses, addresses_string)) >= 0); assert_se(size == 3); - assert_se(!memcmp(&a, &addresses[0], sizeof(struct in_addr))); - assert_se(!memcmp(&b, &addresses[1], sizeof(struct in_addr))); - assert_se(!memcmp(&c, &addresses[2], sizeof(struct in_addr))); + assert_se(in_addr_equal(AF_INET, &a, (union in_addr_union *) &addresses[0])); + assert_se(in_addr_equal(AF_INET, &b, (union in_addr_union *) &addresses[1])); + assert_se(in_addr_equal(AF_INET, &c, (union in_addr_union *) &addresses[2])); assert_se((size = deserialize_in6_addrs(&addresses6, addresses_string)) >= 0); assert_se(size == 3); - assert_se(!memcmp(&d, &addresses6[0], sizeof(struct in6_addr))); - assert_se(!memcmp(&e, &addresses6[1], sizeof(struct in6_addr))); - assert_se(!memcmp(&f, &addresses6[2], sizeof(struct in6_addr))); + assert_se(in_addr_equal(AF_INET6, &d, (union in_addr_union *) &addresses6[0])); + assert_se(in_addr_equal(AF_INET6, &e, (union in_addr_union *) &addresses6[1])); + assert_se(in_addr_equal(AF_INET6, &f, (union in_addr_union *) &addresses6[2])); } static void test_deserialize_dhcp_routes(void) { @@ -121,7 +121,7 @@ static int test_load_config(Manager *manager) { static void test_network_get(Manager *manager, sd_device *loopback) { Network *network; - const struct ether_addr mac = {}; + const struct ether_addr mac = ETHER_ADDR_NULL; /* let's assume that the test machine does not have a .network file that applies to the loopback device... */ @@ -146,13 +146,13 @@ static void test_address_equality(void) { a2->family = AF_INET; assert_se(address_equal(a1, a2)); - assert_se(inet_pton(AF_INET, "192.168.3.9", &a1->in_addr.in)); + assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a1->in_addr) >= 0); assert_se(!address_equal(a1, a2)); - assert_se(inet_pton(AF_INET, "192.168.3.9", &a2->in_addr.in)); + assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a2->in_addr) >= 0); assert_se(address_equal(a1, a2)); - assert_se(inet_pton(AF_INET, "192.168.3.10", &a1->in_addr_peer.in)); + assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a1->in_addr_peer) >= 0); assert_se(address_equal(a1, a2)); - assert_se(inet_pton(AF_INET, "192.168.3.11", &a2->in_addr_peer.in)); + assert_se(in_addr_from_string(AF_INET, "192.168.3.11", &a2->in_addr_peer) >= 0); assert_se(address_equal(a1, a2)); a1->prefixlen = 10; assert_se(!address_equal(a1, a2)); @@ -163,14 +163,14 @@ static void test_address_equality(void) { assert_se(!address_equal(a1, a2)); a2->family = AF_INET6; - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr.in6)); - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr.in6)); + assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr) >= 0); + assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr) >= 0); assert_se(address_equal(a1, a2)); a2->prefixlen = 8; assert_se(address_equal(a1, a2)); - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr.in6)); + assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr) >= 0); assert_se(!address_equal(a1, a2)); } diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 6408719d15..dfb41f801b 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -169,10 +169,12 @@ static void test_config_parse_hwaddr(void) { } static void test_config_parse_address_one(const char *rvalue, int family, unsigned n_addresses, const union in_addr_union *u, unsigned char prefixlen) { - _cleanup_(network_freep) Network *network = NULL; + _cleanup_(network_unrefp) Network *network = NULL; assert_se(network = new0(Network, 1)); + network->n_ref = 1; assert_se(network->filename = strdup("hogehoge.network")); + assert_se(config_parse_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0); assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0); assert_se(network->n_static_addresses == 1); assert_se(network_verify(network) >= 0); diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index e1f9a812fb..e38b5603c2 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -164,7 +164,7 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void * log_link_warning_errno(l, r, "Failed to process RTNL link message, ignoring: %m"); r = link_update_monitor(l); - if (r < 0) + if (r < 0 && r != -ENODATA) log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m"); break; @@ -253,8 +253,8 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * HASHMAP_FOREACH(l, m->links, i) { r = link_update_monitor(l); - if (r < 0) - log_link_warning_errno(l, r, "Failed to update monitor information: %m"); + if (r < 0 && r != -ENODATA) + log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m"); } if (manager_configured(m)) diff --git a/src/notify/notify.c b/src/notify/notify.c index fff5233b0c..750529bbbd 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -151,6 +151,7 @@ static int run(int argc, char* argv[]) { unsigned i = 0; int r; + log_show_color(true); log_parse_environment(); log_open(); @@ -192,7 +193,7 @@ static int run(int argc, char* argv[]) { if (!n) return log_oom(); - /* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave + /* If this is requested change to the requested UID/GID. Note that we only change the real UID here, and leave the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */ diff --git a/src/nspawn/meson.build b/src/nspawn/meson.build index 31217c7b46..c049ac6754 100644 --- a/src/nspawn/meson.build +++ b/src/nspawn/meson.build @@ -48,7 +48,7 @@ tests += [ [['src/nspawn/test-nspawn-tables.c'], [libnspawn_core, libshared], - []], + [libseccomp]], [['src/nspawn/test-patch-uid.c'], [libnspawn_core, diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index 168125d311..ec62b64b00 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -161,7 +161,7 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) * attributes. Hence, let's insert an intermediary cgroup to cover that case too. * * Note that we only bother with the main hierarchy here, not with any secondary ones. On the unified setup - * that's fine because there's only one hiearchy anyway and controllers are enabled directly on it. On the + * that's fine because there's only one hierarchy anyway and controllers are enabled directly on it. On the * legacy setup, this is fine too, since delegation of controllers is generally not safe there, hence we won't * do it. */ diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c index 13ce8490a0..10e6192734 100644 --- a/src/nspawn/nspawn-expose-ports.c +++ b/src/nspawn/nspawn-expose-ports.c @@ -219,7 +219,7 @@ int expose_port_watch_rtnl( r = sd_netlink_attach_event(rtnl, event, 0); if (r < 0) - return log_error_errno(r, "Failed to add to even loop: %m"); + return log_error_errno(r, "Failed to add to event loop: %m"); *ret = TAKE_PTR(rtnl); diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 6040243899..97323f31dd 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -58,6 +58,7 @@ * /bin/mount regarding NFS and FUSE required? * what does terminal=false mean? * sysctl inside or outside? whitelisting? + * swapiness typo -> swappiness * * Unsupported: * diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index c2b779c1d1..4885744cfc 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -103,7 +103,7 @@ static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) { 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 + /* We don't distinguish 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)); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0f64aa68f3..27829431ac 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1495,7 +1495,7 @@ static int verify_arguments(void) { /* We don't support --private-users-chown together with any of the volatile modes since we couldn't * change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a massive - * copy-up (in case of overlay) making the entire excercise pointless. */ + * copy-up (in case of overlay) making the entire exercise pointless. */ if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined."); @@ -3293,7 +3293,7 @@ static int outer_child( /* Mark everything as shared so our mounts get propagated down. This is * required to make new bind mounts available in systemd services - * inside the containter that create a new mount namespace. + * inside the container that create a new mount namespace. * See https://github.com/systemd/systemd/issues/3860 * Further submounts (such as /dev) done after this will inherit the * shared propagation mode. */ @@ -3715,7 +3715,7 @@ static int merge_settings(Settings *settings, const char *path) { /* Copy the full capabilities over too */ if (capability_quintet_is_set(&settings->full_capabilities)) { if (!arg_settings_trusted) - log_warning("Ignoring capabilitiy settings, file %s is not trusted.", path); + log_warning("Ignoring capability settings, file %s is not trusted.", path); else arg_full_capabilities = settings->full_capabilities; } diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index ac5fff523b..95f80572bd 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -169,7 +169,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, allowing falling back to other nss modules. Treat all other error conditions as NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this - case so that the nsswitch.conf configuration can distuingish such executed but + case so that the nsswitch.conf configuration can distinguish such executed but negative replies from complete failure to talk to resolved). */ goto fail; } diff --git a/src/path/path.c b/src/path/path.c index 7b630a5714..568b5fb04b 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -171,6 +171,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char* argv[]) { int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/portable/portable.c b/src/portable/portable.c index 1017864b37..7d39d7b5f3 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -434,7 +434,7 @@ static int portable_extract_by_path( if (r < 0) return log_debug_errno(r, "Failed to receive item: %m"); - /* We can't really distuingish a zero-length datagram without any fds from EOF (both are signalled the + /* We can't really distinguish a zero-length datagram without any fds from EOF (both are signalled the * same way by recvmsg()). Hence, accept either as end notification. */ if (isempty(name) && fd < 0) break; diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 2c59f04eb5..5041745b3d 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -956,6 +956,7 @@ static int run(int argc, char *argv[]) { int r; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c index 383d0b819b..d1f65056d0 100644 --- a/src/resolve/resolvconf-compat.c +++ b/src/resolve/resolvconf-compat.c @@ -172,7 +172,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) { log_debug("Switch -%c ignored.", c); break; - /* Everybody else can agree on the existance of -u but we don't support it. */ + /* Everybody else can agree on the existence of -u but we don't support it. */ case 'u': /* The following options are openresolv inventions we don't support. */ diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 1d2fc71309..9637e00e60 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -3137,6 +3137,7 @@ static int run(int argc, char **argv) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 18e253bea3..997cb986e6 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1106,7 +1106,7 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { DnsResourceRecord *rr; int r; - /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */ + /* Checks whether there's at least one RRSIG in 'a' that protects RRs of the specified key */ DNS_ANSWER_FOREACH(rr, a) { r = dnssec_key_match_rrsig(key, rr); @@ -1985,7 +1985,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r if (have_nsec3) return dnssec_test_nsec3(answer, key, result, authenticated, ttl); - /* No approproate NSEC RR found, report this. */ + /* No appropriate NSEC RR found, report this. */ *result = DNSSEC_NSEC_NO_RR; return 0; } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index a4959cd7c0..20ee8c9ca3 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1949,7 +1949,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_NSEC: { /* - * RFC6762, section 18.14 explictly states mDNS should use name compression. + * RFC6762, section 18.14 explicitly states mDNS should use name compression. * This contradicts RFC3845, section 2.1.1 */ diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 60cd34bcfc..62833efa0e 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -289,7 +289,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo else /* We did not manage to create convert the idna name, or it's * the same as the original name. We assume the caller already - * created an uncoverted question, so let's not repeat work + * created an unconverted question, so let's not repeat work * unnecessarily. */ return -EALREADY; } diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index f072966767..eb304e52e5 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -452,7 +452,7 @@ int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) { } int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) { - /* If ret_socket_address is not NULL, the caller is responisble + /* If ret_socket_address is not NULL, the caller is responsible * for calling connect() or sendmsg(). This is required by TCP * Fast Open, to be able to send the initial SYN packet along * with the first data packet. */ diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 95162845fc..cd6e42e594 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -239,7 +239,7 @@ static void dns_server_reset_counters(DnsServer *s) { * * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A), * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not - * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be + * re-enable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be * incomplete. */ } @@ -879,7 +879,7 @@ void dns_server_unref_stream(DnsServer *s) { /* Detaches the default stream of this server. Some special care needs to be taken here, as that stream and * this server reference each other. First, take the stream out of the server. It's destructor will check if it - * is registered with us, hence let's invalidate this separatly, so that it is already unregistered. */ + * is registered with us, hence let's invalidate this separately, so that it is already unregistered. */ ref = TAKE_PTR(s->stream); /* And then, unref it */ diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index cb7b186fc9..04ba1d91bc 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -61,7 +61,7 @@ static int dns_stream_complete(DnsStream *s, int error) { assert(error >= 0); /* Error is > 0 when the connection failed for some reason in the network stack. It's == 0 if we sent - * and receieved exactly one packet each (in the LLMNR client case). */ + * and received exactly one packet each (in the LLMNR client case). */ #if ENABLE_DNS_OVER_TLS if (s->encrypted) { diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index df42115edc..2ef0234806 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -734,7 +734,7 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) { } /* Fall-through: NXDOMAIN/SERVFAIL is good enough for us. This is because some DNS servers - * erronously return NXDOMAIN/SERVFAIL for empty non-terminals (Akamai...) or missing DS + * erroneously return NXDOMAIN/SERVFAIL for empty non-terminals (Akamai...) or missing DS * records (Facebook), and we need to handle that nicely, when asking for parent SOA or similar * RRs to make unsigned proofs. */ @@ -1502,7 +1502,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { /* * For mDNS, we want to coalesce as many open queries in pending transactions into one single * query packet on the wire as possible. To achieve that, we iterate through all pending transactions - * in our current scope, and see whether their timing contraints allow them to be sent. + * in our current scope, and see whether their timing constraints allow them to be sent. */ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); @@ -2026,7 +2026,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (t->answer_source != DNS_TRANSACTION_NETWORK) return 0; /* We only need to validate stuff from the network */ if (!dns_transaction_dnssec_supported(t)) - return 0; /* If we can't do DNSSEC anyway there's no point in geting the auxiliary RRs */ + return 0; /* If we can't do DNSSEC anyway there's no point in getting the auxiliary RRs */ DNS_ANSWER_FOREACH(rr, t->answer) { @@ -2066,7 +2066,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * RRs for stuff we didn't really ask for, and * also to avoid request loops, where * additional RRs from one transaction result - * in another transaction whose additonal RRs + * in another transaction whose additional RRs * point back to the original transaction, and * we deadlock. */ r = dns_name_endswith(dns_resource_key_name(t->key), rr->rrsig.signer); diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index d25b5f9d95..3e4cc331d9 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -222,7 +222,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u r = dns_name_is_valid(domain); if (r < 0) - return log_warning_errno(r, "Failed to chack validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line); + return log_warning_errno(r, "Failed to check validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line); if (r == 0) { log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line); return -EINVAL; @@ -392,7 +392,7 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u r = dns_name_is_valid(domain); if (r < 0) - return log_warning_errno(r, "Failed to chack validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line); + return log_warning_errno(r, "Failed to check validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line); if (r == 0) { log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line); return -EINVAL; diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index c2d9f3d33d..d5cc2767d7 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -257,7 +257,7 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { /* Check if there's already an RR with the same name * established. If so, it has been probed already, and - * we don't ned to probe again. */ + * we don't need to probe again. */ LIST_FIND_HEAD(by_name, i, first); LIST_FOREACH(by_name, j, first) { diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c index ddf9758bb3..4da5121e62 100644 --- a/src/resolve/resolved-dnstls-gnutls.c +++ b/src/resolve/resolved-dnstls-gnutls.c @@ -120,7 +120,7 @@ int dnstls_stream_shutdown(DnsStream *stream, int error) { assert(stream->encrypted); assert(stream->dnstls_data.session); - /* Store TLS Ticket for faster succesive TLS handshakes */ + /* Store TLS Ticket for faster successive TLS handshakes */ if (stream->server && stream->server->dnstls_data.session_data.size == 0 && stream->dnstls_data.handshake == GNUTLS_E_SUCCESS) gnutls_session_get_data2(stream->dnstls_data.session, &stream->server->dnstls_data.session_data); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 7c8d23d2f8..dd8b5a574b 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -701,7 +701,7 @@ bool link_relevant(Link *l, int family, bool local_multicast) { return false; (void) sd_network_link_get_operational_state(l->ifindex, &state); - if (state && !STR_IN_SET(state, "unknown", "degraded", "routable")) + if (state && !STR_IN_SET(state, "unknown", "degraded", "degraded-carrier", "routable")) return false; LIST_FOREACH(addresses, a, l->addresses) diff --git a/src/run/run.c b/src/run/run.c index 56aa9aaee6..05c1552a8b 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -380,13 +380,31 @@ static int parse_argv(int argc, char *argv[]) { arg_with_timer = true; break; - case ARG_ON_CALENDAR: + case ARG_ON_CALENDAR: { + _cleanup_(calendar_spec_freep) CalendarSpec *cs = NULL; + + /* Let's make sure the given calendar event is not in the past */ + r = calendar_spec_from_string(optarg, &cs); + if (r < 0) + return log_error_errno(r, "Failed to parse calendar event specification: %m"); + + r = calendar_spec_next_usec(cs, now(CLOCK_REALTIME), NULL); + if (r == -ENOENT) + /* The calendar event is in the past — let's warn about this, but install it + * anyway as is. The service manager will trigger the service right away. + * Moreover, the server side might have a different clock or timezone than we + * do, hence it should decide when or whether to run something. */ + log_warning("Specified calendar expression is in the past, proceeding anyway."); + else if (r < 0) + return log_error_errno(r, "Failed to calculate next time calendar expression elapses: %m"); + r = add_timer_property("OnCalendar", optarg); if (r < 0) return r; arg_with_timer = true; break; + } case ARG_ON_TIMEZONE_CHANGE: r = add_timer_property("OnTimezoneChange", "yes"); @@ -930,6 +948,8 @@ typedef struct RunContext { uint64_t cpu_usage_nsec; uint64_t ip_ingress_bytes; uint64_t ip_egress_bytes; + uint64_t io_read_bytes; + uint64_t io_write_bytes; uint32_t exit_code; uint32_t exit_status; } RunContext; @@ -975,6 +995,8 @@ static int run_context_update(RunContext *c, const char *path) { { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) }, { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) }, { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) }, + { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) }, + { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) }, {} }; @@ -1163,6 +1185,8 @@ static int start_transient_service( .cpu_usage_nsec = NSEC_INFINITY, .ip_ingress_bytes = UINT64_MAX, .ip_egress_bytes = UINT64_MAX, + .io_read_bytes = UINT64_MAX, + .io_write_bytes = UINT64_MAX, .inactive_exit_usec = USEC_INFINITY, .inactive_enter_usec = USEC_INFINITY, }; @@ -1262,6 +1286,14 @@ static int start_transient_service( char bytes[FORMAT_BYTES_MAX]; log_info("IP traffic sent: %s", format_bytes(bytes, sizeof(bytes), c.ip_egress_bytes)); } + if (c.io_read_bytes != UINT64_MAX) { + char bytes[FORMAT_BYTES_MAX]; + log_info("IO bytes read: %s", format_bytes(bytes, sizeof(bytes), c.io_read_bytes)); + } + if (c.io_write_bytes != UINT64_MAX) { + char bytes[FORMAT_BYTES_MAX]; + log_info("IO bytes written: %s", format_bytes(bytes, sizeof(bytes), c.io_write_bytes)); + } } /* Try to propagate the service's return value */ @@ -1620,6 +1652,7 @@ static int run(int argc, char* argv[]) { _cleanup_free_ char *description = NULL; int r, retval = EXIT_SUCCESS; + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index ab0c34692f..6c0a369902 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -80,7 +80,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) { if (n < m) break; - explicit_bzero_safe(p, n); + explicit_bzero_safe(p, m); if (m > LONG_MAX / 2) /* overflow check */ return -ENOMEM; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 5c823a3d84..b0c6e4885f 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1083,12 +1083,12 @@ static int verify_esp( * * -ENOENT → if 'searching' is set, and the dir doesn't exist * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP - * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing + * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing */ relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; - /* Non-root user can only check the status, so if an error occured in the following, it does not cause any + /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any * issues. Let's also, silence the error messages. */ if (!relax_checks) { diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index c6cbc9828c..2b425efc9c 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -413,7 +413,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons return 1; } - if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { + if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { if (isempty(eq) || streq(eq, "infinity")) { r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 9c3ce2f712..c2440271fe 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -794,7 +794,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b bus_print_property_value(name, expected_value, value, "[not set]"); - else if ((STR_IN_SET(name, "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || + else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) || (startswith(name, "Limit") && u == (uint64_t) -1) || (startswith(name, "DefaultLimit") && u == (uint64_t) -1)) diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 6e318fa265..4365dbaca8 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -1193,6 +1193,8 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { int tm_usec; int r; + /* Returns -ENOENT if the expression is not going to elapse anymore */ + assert(spec); assert(tm); @@ -1285,14 +1287,13 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { } } -static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) { +static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) { struct tm tm; time_t t; int r; usec_t tm_usec; assert(spec); - assert(next); if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX) return -EINVAL; @@ -1310,7 +1311,9 @@ static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, u if (t < 0) return -EINVAL; - *next = (usec_t) t * USEC_PER_SEC + tm_usec; + if (ret_next) + *ret_next = (usec_t) t * USEC_PER_SEC + tm_usec; + return 0; } @@ -1319,12 +1322,14 @@ typedef struct SpecNextResult { int return_value; } SpecNextResult; -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { +int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) { SpecNextResult *shared, tmp; int r; + assert(spec); + if (isempty(spec->timezone)) - return calendar_spec_next_usec_impl(spec, usec, next); + return calendar_spec_next_usec_impl(spec, usec, ret_next); shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (shared == MAP_FAILED) @@ -1352,8 +1357,8 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) if (munmap(shared, sizeof *shared) < 0) return negative_errno(); - if (tmp.return_value == 0) - *next = tmp.next; + if (tmp.return_value == 0 && ret_next) + *ret_next = tmp.next; return tmp.return_value; } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index ac2f7ceaf8..168fa9cda5 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -48,7 +48,7 @@ int probe_filesystem(const char *node, char **ret_fstype) { /* Try to find device content type and return it in *ret_fstype. If nothing is found, - * 0/NULL will be returned. -EUCLEAN will be returned for ambigous results, and an + * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an * different error otherwise. */ #if HAVE_BLKID diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index ded43f331a..40b8035ac7 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -57,7 +57,7 @@ typedef enum DissectImageFlags { DISSECT_IMAGE_REQUIRE_ROOT = 1 << 5, /* Don't accept disks without root partition */ DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root partition */ DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only non-root partitions */ - DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifyable as OS images */ + DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifiable as OS images */ DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */ } DissectImageFlags; diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 4b31cb36ed..f62ad0a0f5 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -37,7 +37,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f d = dest; for (;;) { - if (*n == 0 || *n == '.') { + if (IN_SET(*n, 0, '.')) { if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') /* Trailing dash */ return -EINVAL; diff --git a/src/shared/efivars.c b/src/shared/efivars.c index fc884dedf0..1bd8835633 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -514,7 +514,7 @@ int efi_get_boot_option( if (path) *path = TAKE_PTR(p); if (active) - *active = !!(header->attr & LOAD_OPTION_ACTIVE); + *active = header->attr & LOAD_OPTION_ACTIVE; return 0; } diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 2867f08a7a..add7fe4c14 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -105,7 +105,7 @@ static int do_execute( return log_oom(); } - /* Abort execution of this process after the timout. We simply rely on SIGALRM as + /* Abort execution of this process after the timeout. We simply rely on SIGALRM as * default action terminating the process, and turn on alarm(). */ if (timeout != USEC_INFINITY) diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 74379e8c64..4f4a84d749 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -845,7 +845,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t } } - /* Generic fallback using the orginal order in which the cells where added. */ + /* Generic fallback using the original order in which the cells where added. */ return CMP(index_a, index_b); } diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 5ff247953b..85e3afce4c 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -75,4 +75,4 @@ const void *table_get(Table *t, TableCell *cell); const void *table_get_at(Table *t, size_t row, size_t column); int table_to_json(Table *t, JsonVariant **ret); -int table_print_json(Table *t, FILE *f, unsigned json_flags); +int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags); diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index 118d591c09..135b01f282 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -121,7 +121,7 @@ int fstab_filter_options(const char *opts, const char *names, t++; continue; found: - /* Keep the last occurence found */ + /* Keep the last occurrence found */ n = name; if (value) { free(v); diff --git a/src/shared/install.c b/src/shared/install.c index ca52d17e4f..5391ac702b 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -888,7 +888,7 @@ static int find_symlinks_in_scope( /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name" * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are - * efectively masked, so we should ignore them. */ + * effectively masked, so we should ignore them. */ STRV_FOREACH(p, paths->search_path) { bool same_name_link = false; @@ -2946,7 +2946,7 @@ static int pattern_match_multiple_instances( int r; /* If no ret is needed or the rule itself does not have instances - * initalized, we return not matching */ + * initialized, we return not matching */ if (!ret || !rule.instances) return 0; diff --git a/src/shared/journal-importer.c b/src/shared/journal-importer.c index 8638cd3cc9..8dc2c42ad1 100644 --- a/src/shared/journal-importer.c +++ b/src/shared/journal-importer.c @@ -94,7 +94,7 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) { c = memchr(imp->buf + start, '\n', imp->filled - start); - if (c != NULL) + if (c) break; } diff --git a/src/shared/journal-util.c b/src/shared/journal-util.c index 89b76af5b3..2f672c2935 100644 --- a/src/shared/journal-util.c +++ b/src/shared/journal-util.c @@ -50,7 +50,7 @@ static int access_check_var_log_journal(sd_journal *j, bool want_other_users) { if (!strv_isempty(g)) { _cleanup_free_ char *s = NULL; - /* Thre are groups in the ACL, let's list them */ + /* There are groups in the ACL, let's list them */ r = strv_extend(&g, "systemd-journal"); if (r < 0) return log_oom(); diff --git a/src/shared/json.c b/src/shared/json.c index a1559bda4e..073f800b34 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -379,9 +379,13 @@ int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n) { assert_return(ret, -EINVAL); if (!s) { - assert_return(n == 0, -EINVAL); + assert_return(IN_SET(n, 0, (size_t) -1), -EINVAL); return json_variant_new_null(ret); } + if (n == (size_t) -1) /* determine length automatically */ + n = strlen(s); + else if (memchr(s, 0, n)) /* don't allow embedded NUL, as we can't express that in JSON */ + return -EINVAL; if (n == 0) { *ret = JSON_VARIANT_MAGIC_EMPTY_STRING; return 0; @@ -585,7 +589,7 @@ int json_variant_new_array_strv(JsonVariant **ret, char **l) { if (k > INLINE_STRING_MAX) { /* If string is too long, store it as reference. */ - r = json_variant_new_stringn(&w->reference, l[v->n_elements], k); + r = json_variant_new_string(&w->reference, l[v->n_elements]); if (r < 0) return r; @@ -3421,7 +3425,7 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags r = strv_extend(&l, json_variant_string(e)); if (r < 0) - return json_log(variant, flags, r, "Failed to append array element: %m"); + return json_log(e, flags, r, "Failed to append array element: %m"); } strv_free_and_replace(*s, l); diff --git a/src/shared/json.h b/src/shared/json.h index 70dfe70dfd..cc2886b0c0 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -12,7 +12,7 @@ /* In case you wonder why we have our own JSON implementation, here are a couple of reasons why this implementation has - benefits over various other implementatins: + benefits over various other implementations: - We need support for 64bit signed and unsigned integers, i.e. the full 64,5bit range of -9223372036854775808…18446744073709551615 - All our variants are immutable after creation @@ -65,7 +65,7 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n); int json_variant_new_null(JsonVariant **ret); static inline int json_variant_new_string(JsonVariant **ret, const char *s) { - return json_variant_new_stringn(ret, s, strlen_ptr(s)); + return json_variant_new_stringn(ret, s, (size_t) -1); } JsonVariant *json_variant_ref(JsonVariant *v); @@ -211,7 +211,7 @@ int json_buildv(JsonVariant **ret, va_list ap); typedef enum JsonDispatchFlags { /* The following three may be set in JsonDispatch's .flags field or the json_dispatch() flags parameter */ JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */ - JSON_MANDATORY = 1 << 1, /* Should existance of this property be mandatory? */ + JSON_MANDATORY = 1 << 1, /* Should existence of this property be mandatory? */ JSON_LOG = 1 << 2, /* Should the parser log about errors? */ /* The following two may be passed into log_json() in addition to the three above */ diff --git a/src/shared/libmount-util.h b/src/shared/libmount-util.h index 7d94468e52..7c3b855df5 100644 --- a/src/shared/libmount-util.h +++ b/src/shared/libmount-util.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <stdio.h> + /* This needs to be after sys/mount.h */ #include <libmount.h> @@ -8,3 +10,38 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); + +static inline int libmount_parse( + const char *path, + FILE *source, + struct libmnt_table **ret_table, + struct libmnt_iter **ret_iter) { + + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; + int r; + + /* Older libmount seems to require this. */ + assert(!source || path); + + table = mnt_new_table(); + iter = mnt_new_iter(MNT_ITER_FORWARD); + if (!table || !iter) + return -ENOMEM; + + /* If source or path are specified, we use on the functions which ignore utab. + * Only if both are empty, we use mnt_table_parse_mtab(). */ + + if (source) + r = mnt_table_parse_stream(table, source, path); + else if (path) + r = mnt_table_parse_file(table, path); + else + r = mnt_table_parse_mtab(table, NULL); + if (r < 0) + return r; + + *ret_table = TAKE_PTR(table); + *ret_iter = TAKE_PTR(iter); + return 0; +} diff --git a/src/shared/linux/bpf.h b/src/shared/linux/bpf.h index 91c43884f2..359fc3703c 100644 --- a/src/shared/linux/bpf.h +++ b/src/shared/linux/bpf.h @@ -951,7 +951,7 @@ union bpf_attr { * Description * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The - * indentifier retrieved is a user-provided tag, similar to the + * identifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 5fb736f633..bdde6c11e3 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -163,21 +163,8 @@ static bool print_multiline( bool ellipsized = false; int line = 0; - if (flags & OUTPUT_COLOR) { - if (priority <= LOG_ERR) { - color_on = ANSI_HIGHLIGHT_RED; - color_off = ANSI_NORMAL; - highlight_on = ANSI_HIGHLIGHT; - } else if (priority <= LOG_NOTICE) { - color_on = ANSI_HIGHLIGHT; - color_off = ANSI_NORMAL; - highlight_on = ANSI_HIGHLIGHT_RED; - } else if (priority >= LOG_DEBUG) { - color_on = ANSI_GREY; - color_off = ANSI_NORMAL; - highlight_on = ANSI_HIGHLIGHT_RED; - } - } + if (flags & OUTPUT_COLOR) + get_log_colors(priority, &color_on, &color_off, &highlight_on); /* A special case: make sure that we print a newline when the message is empty. */ diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 4ad112740d..6b9d8fb97a 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -395,7 +395,7 @@ static int image_make( if (r < 0) return r; - if (size != 0 && size != UINT64_MAX) + if (!IN_SET(size, 0, UINT64_MAX)) (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size; return 0; diff --git a/src/shared/main-func.h b/src/shared/main-func.h index 1b77316d82..6c26cb9fb5 100644 --- a/src/shared/main-func.h +++ b/src/shared/main-func.h @@ -30,7 +30,7 @@ _DEFINE_MAIN_FUNCTION(,impl(argc, argv), r < 0 ? EXIT_FAILURE : EXIT_SUCCESS) /* Zero is mapped to EXIT_SUCCESS, negative values are mapped to EXIT_FAILURE, - * and postive values are propagated. + * and positive values are propagated. * Note: "true" means failure! */ #define DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(impl) \ _DEFINE_MAIN_FUNCTION(,impl(argc, argv), r < 0 ? EXIT_FAILURE : r) diff --git a/src/shared/meson.build b/src/shared/meson.build index c80a67cdde..aa0423ccad 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -164,6 +164,8 @@ shared_sources = files(''' uid-range.c uid-range.h utmp-wtmp.h + varlink.c + varlink.h verbs.c verbs.h vlan-util.c diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 680c4522ad..9129ad5f6b 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -8,16 +8,13 @@ #include <sys/statvfs.h> #include <unistd.h> -/* Include later */ -#include <libmount.h> - #include "alloc-util.h" -#include "escape.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hashmap.h" +#include "libmount-util.h" #include "mount-util.h" #include "mountpoint-util.h" #include "parse-util.h" @@ -36,52 +33,38 @@ int umount_recursive(const char *prefix, int flags) { * unmounting them until they are gone. */ do { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; again = false; - r = fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo); + r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter); if (r < 0) - return r; + return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m"); for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path); - if (k != 1) { - if (k == EOF) - break; + struct libmnt_fs *fs; + const char *path; - continue; - } + r = mnt_table_next_fs(table, iter, &fs); + if (r == 1) + break; + if (r < 0) + return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); - k = cunescape(path, UNESCAPE_RELAX, &p); - if (k < 0) - return k; + path = mnt_fs_get_target(fs); + if (!path) + continue; - if (!path_startswith(p, prefix)) + if (!path_startswith(path, prefix)) continue; - if (umount2(p, flags) < 0) { - r = log_debug_errno(errno, "Failed to umount %s: %m", p); + if (umount2(path, flags) < 0) { + r = log_debug_errno(errno, "Failed to umount %s: %m", path); continue; } - log_debug("Successfully unmounted %s", p); + log_debug("Successfully unmounted %s", path); again = true; n++; @@ -91,7 +74,7 @@ int umount_recursive(const char *prefix, int flags) { } while (again); - return r < 0 ? r : n; + return n; } static int get_mount_flags(const char *path, unsigned long *flags) { @@ -141,6 +124,8 @@ int bind_remount_recursive_with_mountinfo( for (;;) { _cleanup_set_free_free_ Set *todo = NULL; + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; bool top_autofs = false; char *x; unsigned long orig_flags; @@ -151,56 +136,45 @@ int bind_remount_recursive_with_mountinfo( rewind(proc_self_mountinfo); - for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options (superblock) */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options (bind mount) */ - "%*[^\n]", /* some rubbish at the end */ - &path, - &type); - if (k != 2) { - if (k == EOF) - break; + r = libmount_parse("/proc/self/mountinfo", proc_self_mountinfo, &table, &iter); + if (r < 0) + return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m"); - continue; - } + for (;;) { + struct libmnt_fs *fs; + const char *path, *type; - r = cunescape(path, UNESCAPE_RELAX, &p); + r = mnt_table_next_fs(table, iter, &fs); + if (r == 1) + break; if (r < 0) - return r; + return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); - if (!path_startswith(p, cleaned)) + path = mnt_fs_get_target(fs); + type = mnt_fs_get_fstype(fs); + if (!path || !type) continue; - /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall - * operate on. */ - if (!path_equal(cleaned, p)) { + if (!path_startswith(path, cleaned)) + continue; + + /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount + * we shall operate on. */ + if (!path_equal(path, cleaned)) { bool blacklisted = false; char **i; STRV_FOREACH(i, blacklist) { - if (path_equal(*i, cleaned)) continue; if (!path_startswith(*i, cleaned)) continue; - if (path_startswith(p, *i)) { + if (path_startswith(path, *i)) { blacklisted = true; - log_debug("Not remounting %s blacklisted by %s, called for %s", p, *i, cleaned); + log_debug("Not remounting %s blacklisted by %s, called for %s", + path, *i, cleaned); break; } } @@ -215,15 +189,12 @@ int bind_remount_recursive_with_mountinfo( * already triggered, then we will find * another entry for this. */ if (streq(type, "autofs")) { - top_autofs = top_autofs || path_equal(cleaned, p); + top_autofs = top_autofs || path_equal(path, cleaned); continue; } - if (!set_contains(done, p)) { - r = set_consume(todo, p); - p = NULL; - if (r == -EEXIST) - continue; + if (!set_contains(done, path)) { + r = set_put_strdup(todo, path); if (r < 0) return r; } @@ -516,7 +487,7 @@ int mount_option_mangle( * See more examples in test-mount-utils.c. * * Note that if 'options' does not contain any non-mount-flag options, - * then '*ret_remaining_options' is set to NULL instread of empty string. + * then '*ret_remaining_options' is set to NULL instead of empty string. * Note that this does not check validity of options stored in * '*ret_remaining_options'. * Note that if 'options' is NULL, then this just copies 'mount_flags' diff --git a/src/shared/nscd-flush.c b/src/shared/nscd-flush.c index 5a04468d2c..33a06a010c 100644 --- a/src/shared/nscd-flush.c +++ b/src/shared/nscd-flush.c @@ -51,7 +51,7 @@ static int nscd_flush_cache_one(const char *database, usec_t end) { /* Note: connect() returns EINPROGRESS if O_NONBLOCK is set and establishing a connection takes time. The * kernel lets us know this way that the connection is now being established, and we should watch with poll() * to learn when it is fully established. That said, AF_UNIX on Linux never triggers this IRL (connect() is - * always instant on AF_UNIX), hence handling this is mostly just an excercise in defensive, protocol-agnostic + * always instant on AF_UNIX), hence handling this is mostly just an exercise in defensive, protocol-agnostic * programming. * * connect() returns EAGAIN if the socket's backlog limit has been reached. When we see this we give up right @@ -113,7 +113,7 @@ static int nscd_flush_cache_one(const char *database, usec_t end) { if (has_written >= req_size && has_read >= sizeof(resp)) { /* done? */ if (resp < 0) - return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "nscd sent us a negative error numer: %i", resp); + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "nscd sent us a negative error number: %i", resp); if (resp > 0) return log_debug_errno(resp, "nscd return failure code on invalidating '%s'.", database); return 1; diff --git a/src/shared/os-util.c b/src/shared/os-util.c index b2d5ce32e7..2191a610ae 100644 --- a/src/shared/os-util.c +++ b/src/shared/os-util.c @@ -15,7 +15,7 @@ int path_is_os_tree(const char *path) { assert(path); /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir - * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from + * always results in -ENOENT, and we can properly distinguish the case where the whole root doesn't exist from * the case where just the os-release file is missing. */ if (laccess(path, F_OK) < 0) return -errno; diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 7f23e54102..0e114a7b49 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -386,7 +386,7 @@ static bool can_s2h(void) { r = access("/sys/class/rtc/rtc0/wakealarm", W_OK); if (r < 0) { log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "/sys/class/rct/rct0/wakealarm is not writable %m"); + "/sys/class/rtc/rtc0/wakealarm is not writable %m"); return false; } diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index dbb4622312..f721aff760 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -120,10 +120,8 @@ int switch_root(const char *new_root, if (fstat(old_root_fd, &rb) < 0) log_warning_errno(errno, "Failed to stat old root directory, leaving: %m"); - else { - (void) rm_rf_children(old_root_fd, 0, &rb); - old_root_fd = -1; - } + else + (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */ } return 0; diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 4b134b6c8a..646f449821 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -183,16 +183,14 @@ int utmp_put_reboot(usec_t t) { return write_entry_both(&store); } -_pure_ static const char *sanitize_id(const char *id) { +static void copy_suffix(char *buf, size_t buf_size, const char *src) { size_t l; - assert(id); - l = strlen(id); - - if (l <= sizeof(((struct utmpx*) NULL)->ut_id)) - return id; - - return id + l - sizeof(((struct utmpx*) NULL)->ut_id); + l = strlen(src); + if (l < buf_size) + strncpy(buf, src, buf_size); + else + memcpy(buf, src + l - buf_size, buf_size); } int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { @@ -207,11 +205,11 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line init_timestamp(&store, 0); - /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ - strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id)); + /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */ + copy_suffix(store.ut_id, sizeof(store.ut_id), id); if (line) - strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); + strncpy_exact(store.ut_line, line, sizeof(store.ut_line)); r = write_entry_both(&store); if (r < 0) @@ -244,8 +242,8 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { setutxent(); - /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ - strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id)); + /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */ + copy_suffix(store.ut_id, sizeof(store.ut_id), id); found = getutxid(&lookup); if (!found) diff --git a/src/shared/varlink.c b/src/shared/varlink.c new file mode 100644 index 0000000000..5c5f5077f9 --- /dev/null +++ b/src/shared/varlink.c @@ -0,0 +1,2388 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <sys/poll.h> + +#include "alloc-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "list.h" +#include "process-util.h" +#include "set.h" +#include "socket-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "time-util.h" +#include "umask-util.h" +#include "user-util.h" +#include "varlink.h" + +#define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U +#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 1024U + +#define VARLINK_DEFAULT_TIMEOUT_USEC (45U*USEC_PER_SEC) +#define VARLINK_BUFFER_MAX (16U*1024U*1024U) +#define VARLINK_READ_SIZE (64U*1024U) + +typedef enum VarlinkState { + /* Client side states */ + VARLINK_IDLE_CLIENT, + VARLINK_AWAITING_REPLY, + VARLINK_CALLING, + VARLINK_CALLED, + VARLINK_PROCESSING_REPLY, + + /* Server side states */ + VARLINK_IDLE_SERVER, + VARLINK_PROCESSING_METHOD, + VARLINK_PROCESSING_METHOD_MORE, + VARLINK_PROCESSING_METHOD_ONEWAY, + VARLINK_PROCESSED_METHOD, + VARLINK_PROCESSED_METHOD_MORE, + VARLINK_PENDING_METHOD, + VARLINK_PENDING_METHOD_MORE, + + /* Common states (only during shutdown) */ + VARLINK_PENDING_DISCONNECT, + VARLINK_PENDING_TIMEOUT, + VARLINK_PROCESSING_DISCONNECT, + VARLINK_PROCESSING_TIMEOUT, + VARLINK_PROCESSING_FAILURE, + VARLINK_DISCONNECTED, + + _VARLINK_STATE_MAX, + _VARLINK_STATE_INVALID = -1 +} VarlinkState; + +/* Tests whether we are not yet disconnected. Note that this is true during all states where the connection + * is still good for something, and false only when it's dead for good. This means: when we are + * asynchronously connecting to a peer and the connect() is still pending, then this will return 'true', as + * the connection is still good, and we are likely to be able to properly operate on it soon. */ +#define VARLINK_STATE_IS_ALIVE(state) \ + IN_SET(state, \ + VARLINK_IDLE_CLIENT, \ + VARLINK_AWAITING_REPLY, \ + VARLINK_CALLING, \ + VARLINK_CALLED, \ + VARLINK_PROCESSING_REPLY, \ + VARLINK_IDLE_SERVER, \ + VARLINK_PROCESSING_METHOD, \ + VARLINK_PROCESSING_METHOD_MORE, \ + VARLINK_PROCESSING_METHOD_ONEWAY, \ + VARLINK_PROCESSED_METHOD, \ + VARLINK_PROCESSED_METHOD_MORE, \ + VARLINK_PENDING_METHOD, \ + VARLINK_PENDING_METHOD_MORE) + +struct Varlink { + unsigned n_ref; + + VarlinkServer *server; + + VarlinkState state; + bool connecting; /* This boolean indicates whether the socket fd we are operating on is currently + * processing an asynchronous connect(). In that state we watch the socket for + * EPOLLOUT, but we refrain from calling read() or write() on the socket as that + * will trigger ENOTCONN. Note that this boolean is kept separate from the + * VarlinkState above on purpose: while the connect() is still not complete we + * already want to allow queuing of messages and similar. Thus it's nice to keep + * these two state concepts separate: the VarlinkState encodes what our own view of + * the connection is, i.e. whether we think it's a server, a client, and has + * something queued already, while 'connecting' tells us a detail about the + * transport used below, that should have no effect on how we otherwise accept and + * process operations from the user. + * + * Or to say this differently: VARLINK_STATE_IS_ALIVE(state) tells you whether the + * connection is good to use, even if it might not be fully connected + * yet. connecting=true then informs you that actually we are still connecting, and + * the connection is actually not established yet and thus any requests you enqueue + * now will still work fine but will be queued only, not sent yet, but that + * shouldn't stop you from using the connection, since eventually whatever you queue + * *will* be sent. + * + * Or to say this even differently: 'state' is a high-level ("application layer" + * high, if you so will) state, while 'conecting' is a low-level ("transport layer" + * low, if you so will) state, and while they are not entirely unrelated and + * sometimes propagate effects to each other they are only asynchronously connected + * at most. */ + unsigned n_pending; + + int fd; + + char *input_buffer; /* valid data starts at input_buffer_index, ends at input_buffer_index+input_buffer_size */ + size_t input_buffer_allocated; + size_t input_buffer_index; + size_t input_buffer_size; + size_t input_buffer_unscanned; + + char *output_buffer; /* valid data starts at output_buffer_index, ends at output_buffer_index+output_buffer_size */ + size_t output_buffer_allocated; + size_t output_buffer_index; + size_t output_buffer_size; + + VarlinkReply reply_callback; + + JsonVariant *current; + JsonVariant *reply; + + struct ucred ucred; + bool ucred_acquired:1; + + bool write_disconnected:1; + bool read_disconnected:1; + bool prefer_read_write:1; + bool got_pollhup:1; + + usec_t timestamp; + usec_t timeout; + + void *userdata; + char *description; + + sd_event *event; + sd_event_source *io_event_source; + sd_event_source *time_event_source; + sd_event_source *quit_event_source; + sd_event_source *defer_event_source; +}; + +typedef struct VarlinkServerSocket VarlinkServerSocket; + +struct VarlinkServerSocket { + VarlinkServer *server; + + int fd; + char *address; + + sd_event_source *event_source; + + LIST_FIELDS(VarlinkServerSocket, sockets); +}; + +struct VarlinkServer { + unsigned n_ref; + VarlinkServerFlags flags; + + LIST_HEAD(VarlinkServerSocket, sockets); + + Hashmap *methods; + VarlinkConnect connect_callback; + + sd_event *event; + int64_t event_priority; + + unsigned n_connections; + Hashmap *by_uid; + + void *userdata; + char *description; + + unsigned connections_max; + unsigned connections_per_uid_max; +}; + +static const char* const varlink_state_table[_VARLINK_STATE_MAX] = { + [VARLINK_IDLE_CLIENT] = "idle-client", + [VARLINK_AWAITING_REPLY] = "awaiting-reply", + [VARLINK_CALLING] = "calling", + [VARLINK_CALLED] = "called", + [VARLINK_PROCESSING_REPLY] = "processing-reply", + [VARLINK_IDLE_SERVER] = "idle-server", + [VARLINK_PROCESSING_METHOD] = "processing-method", + [VARLINK_PROCESSING_METHOD_MORE] = "processing-method-more", + [VARLINK_PROCESSING_METHOD_ONEWAY] = "processing-method-oneway", + [VARLINK_PROCESSED_METHOD] = "processed-method", + [VARLINK_PROCESSED_METHOD_MORE] = "processed-method-more", + [VARLINK_PENDING_METHOD] = "pending-method", + [VARLINK_PENDING_METHOD_MORE] = "pending-method-more", + [VARLINK_PENDING_DISCONNECT] = "pending-disconnect", + [VARLINK_PENDING_TIMEOUT] = "pending-timeout", + [VARLINK_PROCESSING_DISCONNECT] = "processing-disconnect", + [VARLINK_PROCESSING_TIMEOUT] = "processing-timeout", + [VARLINK_PROCESSING_FAILURE] = "processing-failure", + [VARLINK_DISCONNECTED] = "disconnected", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(varlink_state, VarlinkState); + +#define varlink_log_errno(v, error, fmt, ...) \ + log_debug_errno(error, "%s: " fmt, varlink_description(v), ##__VA_ARGS__) + +#define varlink_log(v, fmt, ...) \ + log_debug("%s: " fmt, varlink_description(v), ##__VA_ARGS__) + +#define varlink_server_log_errno(s, error, fmt, ...) \ + log_debug_errno(error, "%s: " fmt, varlink_server_description(s), ##__VA_ARGS__) + +#define varlink_server_log(s, fmt, ...) \ + log_debug("%s: " fmt, varlink_server_description(s), ##__VA_ARGS__) + +static inline const char *varlink_description(Varlink *v) { + return strna(v ? v->description : NULL); +} + +static inline const char *varlink_server_description(VarlinkServer *s) { + return strna(s ? s->description : NULL); +} + +static void varlink_set_state(Varlink *v, VarlinkState state) { + assert(v); + assert(state >= 0 && state < _VARLINK_STATE_MAX); + + if (v->state < 0) + varlink_log(v, "varlink: setting state %s", + varlink_state_to_string(state)); + else + varlink_log(v, "varlink: changing state %s → %s", + varlink_state_to_string(v->state), + varlink_state_to_string(state)); + + v->state = state; +} + +static int varlink_new(Varlink **ret) { + Varlink *v; + + assert(ret); + + v = new(Varlink, 1); + if (!v) + return -ENOMEM; + + *v = (Varlink) { + .n_ref = 1, + .fd = -1, + + .state = _VARLINK_STATE_INVALID, + + .ucred.uid = UID_INVALID, + .ucred.gid = GID_INVALID, + + .timestamp = USEC_INFINITY, + .timeout = VARLINK_DEFAULT_TIMEOUT_USEC + }; + + *ret = v; + return 0; +} + +int varlink_connect_address(Varlink **ret, const char *address) { + _cleanup_(varlink_unrefp) Varlink *v = NULL; + union sockaddr_union sockaddr; + int r; + + assert_return(ret, -EINVAL); + assert_return(address, -EINVAL); + + r = sockaddr_un_set_path(&sockaddr.un, address); + if (r < 0) + return r; + + r = varlink_new(&v); + if (r < 0) + return r; + + v->fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (v->fd < 0) + return -errno; + + if (connect(v->fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) { + if (!IN_SET(errno, EAGAIN, EINPROGRESS)) + return -errno; + + v->connecting = true; /* We are asynchronously connecting, i.e. the connect() is being + * processed in the background. As long as that's the case the socket + * is in a special state: it's there, we can poll it for EPOLLOUT, but + * if we attempt to write() to it before we see EPOLLOUT we'll get + * ENOTCONN (and not EAGAIN, like we would for a normal connected + * socket that isn't writable at the moment). Since ENOTCONN on write() + * hence can mean two different things (i.e. connection not complete + * yet vs. already disconnected again), we store as a boolean whether + * we are still in connect(). */ + } + + varlink_set_state(v, VARLINK_IDLE_CLIENT); + + *ret = TAKE_PTR(v); + return r; +} + +int varlink_connect_fd(Varlink **ret, int fd) { + Varlink *v; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + r = varlink_new(&v); + if (r < 0) + return r; + + v->fd = fd; + varlink_set_state(v, VARLINK_IDLE_CLIENT); + + /* Note that if this function is called we assume the passed socket (if it is one) is already + * properly connected, i.e. any asynchronous connect() done on it already completed. Because of that + * we'll not set the 'connecting' boolean here, i.e. we don't need to avoid write()ing to the socket + * until the connection is fully set up. Behaviour here is hence a bit different from + * varlink_connect_address() above, as there we do handle asynchronous connections ourselves and + * avoid doing write() on it before we saw EPOLLOUT for the first time. */ + + *ret = v; + return 0; +} + +static void varlink_detach_event_sources(Varlink *v) { + assert(v); + + v->io_event_source = sd_event_source_disable_unref(v->io_event_source); + + v->time_event_source = sd_event_source_disable_unref(v->time_event_source); + + v->quit_event_source = sd_event_source_disable_unref(v->quit_event_source); + + v->defer_event_source = sd_event_source_disable_unref(v->defer_event_source); +} + +static void varlink_clear(Varlink *v) { + assert(v); + + varlink_detach_event_sources(v); + + v->fd = safe_close(v->fd); + + v->input_buffer = mfree(v->input_buffer); + v->output_buffer = mfree(v->output_buffer); + + v->current = json_variant_unref(v->current); + v->reply = json_variant_unref(v->reply); + + v->event = sd_event_unref(v->event); +} + +static Varlink* varlink_destroy(Varlink *v) { + if (!v) + return NULL; + + /* If this is called the server object must already been unreffed here. Why that? because when we + * linked up the varlink connection with the server object we took one ref in each direction */ + assert(!v->server); + + varlink_clear(v); + + free(v->description); + return mfree(v); +} + +DEFINE_TRIVIAL_REF_UNREF_FUNC(Varlink, varlink, varlink_destroy); + +static int varlink_test_disconnect(Varlink *v) { + assert(v); + + /* Tests whether we the the connection has been terminated. We are careful to not stop processing it + * prematurely, since we want to handle half-open connections as well as possible and want to flush + * out and read data before we close down if we can. */ + + /* Already disconnected? */ + if (!VARLINK_STATE_IS_ALIVE(v->state)) + return 0; + + /* Wait until connection setup is complete, i.e. until asynchronous connect() completes */ + if (v->connecting) + return 0; + + /* Still something to write and we can write? Stay around */ + if (v->output_buffer_size > 0 && !v->write_disconnected) + return 0; + + /* Both sides gone already? Then there's no need to stick around */ + if (v->read_disconnected && v->write_disconnected) + goto disconnect; + + /* If we are waiting for incoming data but the read side is shut down, disconnect. */ + if (IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING, VARLINK_IDLE_SERVER) && v->read_disconnected) + goto disconnect; + + /* Similar, if are a client that hasn't written anything yet but the write side is dead, also + * disconnect. We also explicitly check for POLLHUP here since we likely won't notice the write side + * being down if we never wrote anything. */ + if (IN_SET(v->state, VARLINK_IDLE_CLIENT) && (v->write_disconnected || v->got_pollhup)) + goto disconnect; + + return 0; + +disconnect: + varlink_set_state(v, VARLINK_PENDING_DISCONNECT); + return 1; +} + +static int varlink_write(Varlink *v) { + ssize_t n; + + assert(v); + + if (!VARLINK_STATE_IS_ALIVE(v->state)) + return 0; + if (v->connecting) /* Writing while we are still wait for a non-blocking connect() to complete will + * result in ENOTCONN, hence exit early here */ + return 0; + if (v->output_buffer_size == 0) + return 0; + if (v->write_disconnected) + return 0; + + assert(v->fd >= 0); + + /* We generally prefer recv()/send() (mostly because of MSG_NOSIGNAL) but also want to be compatible + * with non-socket IO, hence fall back automatically */ + if (!v->prefer_read_write) { + n = send(v->fd, v->output_buffer + v->output_buffer_index, v->output_buffer_size, MSG_DONTWAIT|MSG_NOSIGNAL); + if (n < 0 && errno == ENOTSOCK) + v->prefer_read_write = true; + } + if (v->prefer_read_write) + n = write(v->fd, v->output_buffer + v->output_buffer_index, v->output_buffer_size); + if (n < 0) { + if (errno == EAGAIN) + return 0; + + if (ERRNO_IS_DISCONNECT(errno)) { + /* If we get informed about a disconnect on write, then let's remember that, but not + * act on it just yet. Let's wait for read() to report the issue first. */ + v->write_disconnected = true; + return 1; + } + + return -errno; + } + + v->output_buffer_size -= n; + + if (v->output_buffer_size == 0) + v->output_buffer_index = 0; + else + v->output_buffer_index += n; + + v->timestamp = now(CLOCK_MONOTONIC); + return 1; +} + +static int varlink_read(Varlink *v) { + size_t rs; + ssize_t n; + + assert(v); + + if (!IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING, VARLINK_IDLE_SERVER)) + return 0; + if (v->connecting) /* read() on a socket while we are in connect() will fail with EINVAL, hence exit early here */ + return 0; + if (v->current) + return 0; + if (v->input_buffer_unscanned > 0) + return 0; + if (v->read_disconnected) + return 0; + + if (v->input_buffer_size >= VARLINK_BUFFER_MAX) + return -ENOBUFS; + + assert(v->fd >= 0); + + if (v->input_buffer_allocated <= v->input_buffer_index + v->input_buffer_size) { + size_t add; + + add = MIN(VARLINK_BUFFER_MAX - v->input_buffer_size, VARLINK_READ_SIZE); + + if (v->input_buffer_index == 0) { + + if (!GREEDY_REALLOC(v->input_buffer, v->input_buffer_allocated, v->input_buffer_size + add)) + return -ENOMEM; + + } else { + char *b; + + b = new(char, v->input_buffer_size + add); + if (!b) + return -ENOMEM; + + memcpy(b, v->input_buffer + v->input_buffer_index, v->input_buffer_size); + + free_and_replace(v->input_buffer, b); + + v->input_buffer_allocated = v->input_buffer_size + add; + v->input_buffer_index = 0; + } + } + + rs = v->input_buffer_allocated - (v->input_buffer_index + v->input_buffer_size); + + if (!v->prefer_read_write) { + n = recv(v->fd, v->input_buffer + v->input_buffer_index + v->input_buffer_size, rs, MSG_DONTWAIT); + if (n < 0 && errno == ENOTSOCK) + v->prefer_read_write = true; + } + if (v->prefer_read_write) + n = read(v->fd, v->input_buffer + v->input_buffer_index + v->input_buffer_size, rs); + if (n < 0) { + if (errno == EAGAIN) + return 0; + + if (ERRNO_IS_DISCONNECT(errno)) { + v->read_disconnected = true; + return 1; + } + + return -errno; + } + if (n == 0) { /* EOF */ + v->read_disconnected = true; + return 1; + } + + v->input_buffer_size += n; + v->input_buffer_unscanned += n; + + return 1; +} + +static int varlink_parse_message(Varlink *v) { + const char *e, *begin; + size_t sz; + int r; + + assert(v); + + if (v->current) + return 0; + if (v->input_buffer_unscanned <= 0) + return 0; + + assert(v->input_buffer_unscanned <= v->input_buffer_size); + assert(v->input_buffer_index + v->input_buffer_size <= v->input_buffer_allocated); + + begin = v->input_buffer + v->input_buffer_index; + + e = memchr(begin + v->input_buffer_size - v->input_buffer_unscanned, 0, v->input_buffer_unscanned); + if (!e) { + v->input_buffer_unscanned = 0; + return 0; + } + + sz = e - begin + 1; + + varlink_log(v, "New incoming message: %s", begin); + + r = json_parse(begin, &v->current, NULL, NULL); + if (r < 0) + return r; + + v->input_buffer_size -= sz; + + if (v->input_buffer_size == 0) + v->input_buffer_index = 0; + else + v->input_buffer_index += sz; + + v->input_buffer_unscanned = v->input_buffer_size; + return 1; +} + +static int varlink_test_timeout(Varlink *v) { + assert(v); + + if (!IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING)) + return 0; + if (v->timeout == USEC_INFINITY) + return 0; + + if (now(CLOCK_MONOTONIC) < usec_add(v->timestamp, v->timeout)) + return 0; + + varlink_set_state(v, VARLINK_PENDING_TIMEOUT); + + return 1; +} + +static int varlink_dispatch_local_error(Varlink *v, const char *error) { + int r; + + assert(v); + assert(error); + + if (!v->reply_callback) + return 0; + + r = v->reply_callback(v, NULL, error, VARLINK_REPLY_ERROR|VARLINK_REPLY_LOCAL, v->userdata); + if (r < 0) + log_debug_errno(r, "Reply callback returned error, ignoring: %m"); + + return 1; +} + +static int varlink_dispatch_timeout(Varlink *v) { + assert(v); + + if (v->state != VARLINK_PENDING_TIMEOUT) + return 0; + + varlink_set_state(v, VARLINK_PROCESSING_TIMEOUT); + varlink_dispatch_local_error(v, VARLINK_ERROR_TIMEOUT); + varlink_close(v); + + return 1; +} + +static int varlink_dispatch_disconnect(Varlink *v) { + assert(v); + + if (v->state != VARLINK_PENDING_DISCONNECT) + return 0; + + varlink_set_state(v, VARLINK_PROCESSING_DISCONNECT); + varlink_dispatch_local_error(v, VARLINK_ERROR_DISCONNECTED); + varlink_close(v); + + return 1; +} + +static int varlink_sanitize_parameters(JsonVariant **v) { + assert(v); + + /* Varlink always wants a parameters list, hence make one if the caller doesn't want any */ + if (!*v) + return json_variant_new_object(v, NULL, 0); + else if (!json_variant_is_object(*v)) + return -EINVAL; + + return 0; +} + +static int varlink_dispatch_reply(Varlink *v) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + VarlinkReplyFlags flags = 0; + const char *error = NULL; + JsonVariant *e; + const char *k; + int r; + + assert(v); + + if (!IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING)) + return 0; + if (!v->current) + return 0; + + assert(v->n_pending > 0); + + if (!json_variant_is_object(v->current)) + goto invalid; + + JSON_VARIANT_OBJECT_FOREACH(k, e, v->current) { + + if (streq(k, "error")) { + if (error) + goto invalid; + if (!json_variant_is_string(e)) + goto invalid; + + error = json_variant_string(e); + flags |= VARLINK_REPLY_ERROR; + + } else if (streq(k, "parameters")) { + if (parameters) + goto invalid; + if (!json_variant_is_object(e)) + goto invalid; + + parameters = json_variant_ref(e); + + } else if (streq(k, "continues")) { + if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES)) + goto invalid; + + if (!json_variant_is_boolean(e)) + goto invalid; + + if (json_variant_boolean(e)) + flags |= VARLINK_REPLY_CONTINUES; + } else + goto invalid; + } + + if (error && FLAGS_SET(flags, VARLINK_REPLY_CONTINUES)) + goto invalid; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + goto invalid; + + if (v->state == VARLINK_AWAITING_REPLY) { + varlink_set_state(v, VARLINK_PROCESSING_REPLY); + + if (v->reply_callback) { + r = v->reply_callback(v, parameters, error, flags, v->userdata); + if (r < 0) + log_debug_errno(r, "Reply callback returned error, ignoring: %m"); + } + + v->current = json_variant_unref(v->current); + + if (v->state == VARLINK_PROCESSING_REPLY) { + assert(v->n_pending > 0); + v->n_pending--; + + varlink_set_state(v, v->n_pending == 0 ? VARLINK_IDLE_CLIENT : VARLINK_AWAITING_REPLY); + } + } else { + assert(v->state == VARLINK_CALLING); + + if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES)) + goto invalid; + + varlink_set_state(v, VARLINK_CALLED); + } + + return 1; + +invalid: + varlink_set_state(v, VARLINK_PROCESSING_FAILURE); + varlink_dispatch_local_error(v, VARLINK_ERROR_PROTOCOL); + varlink_close(v); + + return 1; +} + +static int varlink_dispatch_method(Varlink *v) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + VarlinkMethodFlags flags = 0; + const char *method = NULL, *error; + JsonVariant *e; + VarlinkMethod callback; + const char *k; + int r; + + assert(v); + + if (v->state != VARLINK_IDLE_SERVER) + return 0; + if (!v->current) + return 0; + + if (!json_variant_is_object(v->current)) + goto invalid; + + JSON_VARIANT_OBJECT_FOREACH(k, e, v->current) { + + if (streq(k, "method")) { + if (method) + goto invalid; + if (!json_variant_is_string(e)) + goto invalid; + + method = json_variant_string(e); + + } else if (streq(k, "parameters")) { + if (parameters) + goto invalid; + if (!json_variant_is_object(e)) + goto invalid; + + parameters = json_variant_ref(e); + + } else if (streq(k, "oneway")) { + + if ((flags & (VARLINK_METHOD_ONEWAY|VARLINK_METHOD_MORE)) != 0) + goto invalid; + + if (!json_variant_is_boolean(e)) + goto invalid; + + if (json_variant_boolean(e)) + flags |= VARLINK_METHOD_ONEWAY; + + } else if (streq(k, "more")) { + + if ((flags & (VARLINK_METHOD_ONEWAY|VARLINK_METHOD_MORE)) != 0) + goto invalid; + + if (!json_variant_is_boolean(e)) + goto invalid; + + if (json_variant_boolean(e)) + flags |= VARLINK_METHOD_MORE; + + } else + goto invalid; + } + + if (!method) + goto invalid; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + goto fail; + + varlink_set_state(v, (flags & VARLINK_METHOD_MORE) ? VARLINK_PROCESSING_METHOD_MORE : + (flags & VARLINK_METHOD_ONEWAY) ? VARLINK_PROCESSING_METHOD_ONEWAY : + VARLINK_PROCESSING_METHOD); + + assert(v->server); + + if (STR_IN_SET(method, "org.varlink.service.GetInfo", "org.varlink.service.GetInterface")) { + /* For now, we don't implement a single of varlink's own methods */ + callback = NULL; + error = VARLINK_ERROR_METHOD_NOT_IMPLEMENTED; + } else if (startswith(method, "org.varlink.service.")) { + callback = NULL; + error = VARLINK_ERROR_METHOD_NOT_FOUND; + } else { + callback = hashmap_get(v->server->methods, method); + error = VARLINK_ERROR_METHOD_NOT_FOUND; + } + + if (callback) { + r = callback(v, parameters, flags, v->userdata); + if (r < 0) { + log_debug_errno(r, "Callback for %s returned error: %m", method); + + /* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */ + if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { + r = varlink_errorb(v, VARLINK_ERROR_SYSTEM, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("errno", JSON_BUILD_INTEGER(-r)))); + if (r < 0) + return r; + } + } + } else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { + assert(error); + + r = varlink_errorb(v, error, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method)))); + if (r < 0) + return r; + } + + switch (v->state) { + + case VARLINK_PROCESSED_METHOD: /* Method call is fully processed */ + case VARLINK_PROCESSING_METHOD_ONEWAY: /* ditto */ + v->current = json_variant_unref(v->current); + varlink_set_state(v, VARLINK_IDLE_SERVER); + break; + + case VARLINK_PROCESSING_METHOD: /* Method call wasn't replied to, will be replied to later */ + varlink_set_state(v, VARLINK_PENDING_METHOD); + break; + + case VARLINK_PROCESSED_METHOD_MORE: /* One reply for a "more" message was sent, more to come */ + case VARLINK_PROCESSING_METHOD_MORE: /* No reply for a "more" message was sent, more to come */ + varlink_set_state(v, VARLINK_PENDING_METHOD_MORE); + break; + + default: + assert_not_reached("Unexpected state"); + + } + + return r; + +invalid: + r = -EINVAL; + +fail: + varlink_set_state(v, VARLINK_PROCESSING_FAILURE); + varlink_dispatch_local_error(v, VARLINK_ERROR_PROTOCOL); + varlink_close(v); + + return r; +} + +int varlink_process(Varlink *v) { + int r; + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + + varlink_ref(v); + + r = varlink_write(v); + if (r != 0) + goto finish; + + r = varlink_dispatch_reply(v); + if (r != 0) + goto finish; + + r = varlink_dispatch_method(v); + if (r != 0) + goto finish; + + r = varlink_parse_message(v); + if (r != 0) + goto finish; + + r = varlink_read(v); + if (r != 0) + goto finish; + + r = varlink_test_disconnect(v); + if (r != 0) + goto finish; + + r = varlink_dispatch_disconnect(v); + if (r != 0) + goto finish; + + r = varlink_test_timeout(v); + if (r != 0) + goto finish; + + r = varlink_dispatch_timeout(v); + if (r != 0) + goto finish; + +finish: + if (r >= 0 && v->defer_event_source) { + int q; + + /* If we did some processing, make sure we are called again soon */ + q = sd_event_source_set_enabled(v->defer_event_source, r > 0 ? SD_EVENT_ON : SD_EVENT_OFF); + if (q < 0) + r = q; + } + + if (r < 0) { + if (VARLINK_STATE_IS_ALIVE(v->state)) + /* Initiate disconnection */ + varlink_set_state(v, VARLINK_PENDING_DISCONNECT); + else + /* We failed while disconnecting, in that case close right away */ + varlink_close(v); + } + + varlink_unref(v); + return r; +} + +static void handle_revents(Varlink *v, int revents) { + assert(v); + + if (v->connecting) { + /* If we have seen POLLOUT or POLLHUP on a socket we are asynchronously waiting a connect() + * to complete on, we know we are ready. We don't read the connection error here though, + * we'll get the error on the next read() or write(). */ + if ((revents & (POLLOUT|POLLHUP)) == 0) + return; + + varlink_log(v, "Anynchronous connection completed."); + v->connecting = false; + } else { + /* Note that we don't care much about POLLIN/POLLOUT here, we'll just try reading and writing + * what we can. However, we do care about POLLHUP to detect connection termination even if we + * momentarily don't want to read nor write anything. */ + + if (!FLAGS_SET(revents, POLLHUP)) + return; + + varlink_log(v, "Got POLLHUP from socket."); + v->got_pollhup = true; + } +} + +int varlink_wait(Varlink *v, usec_t timeout) { + struct timespec ts; + struct pollfd pfd; + int r, fd, events; + usec_t t; + + assert_return(v, -EINVAL); + assert_return(!v->server, -ENOTTY); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + + r = varlink_get_timeout(v, &t); + if (r < 0) + return r; + if (t != USEC_INFINITY) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (t < n) + t = 0; + else + t = usec_sub_unsigned(t, n); + } + + if (timeout != USEC_INFINITY && + (t == USEC_INFINITY || timeout < t)) + t = timeout; + + fd = varlink_get_fd(v); + if (fd < 0) + return fd; + + events = varlink_get_events(v); + if (events < 0) + return events; + + pfd = (struct pollfd) { + .fd = fd, + .events = events, + }; + + r = ppoll(&pfd, 1, + t == USEC_INFINITY ? NULL : timespec_store(&ts, t), + NULL); + if (r < 0) + return -errno; + + handle_revents(v, pfd.revents); + + return r > 0 ? 1 : 0; +} + +int varlink_get_fd(Varlink *v) { + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (v->fd < 0) + return -EBADF; + + return v->fd; +} + +int varlink_get_events(Varlink *v) { + int ret = 0; + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + + if (v->connecting) /* When processing an asynchronous connect(), we only wait for EPOLLOUT, which + * tells us that the connection is now complete. Before that we should neither + * write() or read() from the fd. */ + return EPOLLOUT; + + if (!v->read_disconnected && + IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING, VARLINK_IDLE_SERVER) && + !v->current && + v->input_buffer_unscanned <= 0) + ret |= EPOLLIN; + + if (!v->write_disconnected && + v->output_buffer_size > 0) + ret |= EPOLLOUT; + + return ret; +} + +int varlink_get_timeout(Varlink *v, usec_t *ret) { + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + + if (IN_SET(v->state, VARLINK_AWAITING_REPLY, VARLINK_CALLING) && + v->timeout != USEC_INFINITY) { + if (ret) + *ret = usec_add(v->timestamp, v->timeout); + return 1; + } else { + if (ret) + *ret = USEC_INFINITY; + return 0; + } +} + +int varlink_flush(Varlink *v) { + int ret = 0, r; + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + + for (;;) { + struct pollfd pfd; + + if (v->output_buffer_size == 0) + break; + if (v->write_disconnected) + return -ECONNRESET; + + r = varlink_write(v); + if (r < 0) + return r; + if (r > 0) { + ret = 1; + continue; + } + + pfd = (struct pollfd) { + .fd = v->fd, + .events = POLLOUT, + }; + + if (poll(&pfd, 1, -1) < 0) + return -errno; + + handle_revents(v, pfd.revents); + } + + return ret; +} + +static void varlink_detach_server(Varlink *v) { + assert(v); + + if (!v->server) + return; + + if (v->server->by_uid && + v->ucred_acquired && + uid_is_valid(v->ucred.uid)) { + unsigned c; + + c = PTR_TO_UINT(hashmap_get(v->server->by_uid, UID_TO_PTR(v->ucred.uid))); + assert(c > 0); + + if (c == 1) + (void) hashmap_remove(v->server->by_uid, UID_TO_PTR(v->ucred.uid)); + else + (void) hashmap_replace(v->server->by_uid, UID_TO_PTR(v->ucred.uid), UINT_TO_PTR(c - 1)); + } + + assert(v->server->n_connections > 0); + v->server->n_connections--; + + /* If this is a connection associated to a server, then let's disconnect the server and the + * connection from each other. This drops the dangling reference that connect_callback() set up. */ + v->server = varlink_server_unref(v->server); + varlink_unref(v); +} + +int varlink_close(Varlink *v) { + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return 0; + + varlink_set_state(v, VARLINK_DISCONNECTED); + + /* Let's take a reference first, since varlink_detach_server() might drop the final (dangling) ref + * which would destroy us before we can call varlink_clear() */ + varlink_ref(v); + varlink_detach_server(v); + varlink_clear(v); + varlink_unref(v); + + return 1; +} + +Varlink* varlink_flush_close_unref(Varlink *v) { + + if (!v) + return NULL; + + (void) varlink_flush(v); + (void) varlink_close(v); + + return varlink_unref(v); +} + +static int varlink_enqueue_json(Varlink *v, JsonVariant *m) { + _cleanup_free_ char *text = NULL; + int r; + + assert(v); + assert(m); + + r = json_variant_format(m, 0, &text); + if (r < 0) + return r; + + if (v->output_buffer_size + r + 1 > VARLINK_BUFFER_MAX) + return -ENOBUFS; + + varlink_log(v, "Sending message: %s", text); + + if (v->output_buffer_size == 0) { + + free_and_replace(v->output_buffer, text); + + v->output_buffer_size = v->output_buffer_allocated = r + 1; + v->output_buffer_index = 0; + + } else if (v->output_buffer_index == 0) { + + if (!GREEDY_REALLOC(v->output_buffer, v->output_buffer_allocated, v->output_buffer_size + r + 1)) + return -ENOMEM; + + memcpy(v->output_buffer + v->output_buffer_size, text, r + 1); + v->output_buffer_size += r + 1; + + } else { + char *n; + + n = new(char, v->output_buffer_size + r + 1); + if (!n) + return -ENOMEM; + + memcpy(mempcpy(n, v->output_buffer + v->output_buffer_index, v->output_buffer_size), text, r + 1); + + free_and_replace(v->output_buffer, n); + v->output_buffer_size += r + 1; + v->output_buffer_index = 0; + } + + return 0; +} + +int varlink_send(Varlink *v, const char *method, JsonVariant *parameters) { + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + assert_return(method, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY)) + return -EBUSY; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method)), + JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)), + JSON_BUILD_PAIR("oneway", JSON_BUILD_BOOLEAN(true)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + /* No state change here, this is one-way only after all */ + v->timestamp = now(CLOCK_MONOTONIC); + return 0; +} + +int varlink_sendb(Varlink *v, const char *method, ...) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + + va_start(ap, method); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_send(v, method, parameters); +} + +int varlink_invoke(Varlink *v, const char *method, JsonVariant *parameters) { + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + assert_return(method, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY)) + return -EBUSY; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method)), + JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + varlink_set_state(v, VARLINK_AWAITING_REPLY); + v->n_pending++; + v->timestamp = now(CLOCK_MONOTONIC); + + return 0; +} + +int varlink_invokeb(Varlink *v, const char *method, ...) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + + va_start(ap, method); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_invoke(v, method, parameters); +} + +int varlink_call( + Varlink *v, + const char *method, + JsonVariant *parameters, + JsonVariant **ret_parameters, + const char **ret_error_id, + VarlinkReplyFlags *ret_flags) { + + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + assert_return(method, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, VARLINK_IDLE_CLIENT)) + return -EBUSY; + + assert(v->n_pending == 0); /* n_pending can't be > 0 if we are in VARLINK_IDLE_CLIENT state */ + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method)), + JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + varlink_set_state(v, VARLINK_CALLING); + v->n_pending++; + v->timestamp = now(CLOCK_MONOTONIC); + + while (v->state == VARLINK_CALLING) { + + r = varlink_process(v); + if (r < 0) + return r; + if (r > 0) + continue; + + r = varlink_wait(v, USEC_INFINITY); + if (r < 0) + return r; + } + + switch (v->state) { + + case VARLINK_CALLED: + assert(v->current); + + json_variant_unref(v->reply); + v->reply = TAKE_PTR(v->current); + + varlink_set_state(v, VARLINK_IDLE_CLIENT); + assert(v->n_pending == 1); + v->n_pending--; + + if (ret_parameters) + *ret_parameters = json_variant_by_key(v->reply, "parameters"); + if (ret_error_id) + *ret_error_id = json_variant_string(json_variant_by_key(v->reply, "error")); + if (ret_flags) + *ret_flags = 0; + + return 1; + + case VARLINK_PENDING_DISCONNECT: + case VARLINK_DISCONNECTED: + return -ECONNRESET; + + case VARLINK_PENDING_TIMEOUT: + return -ETIME; + + default: + assert_not_reached("Unexpected state after method call."); + } +} + +int varlink_callb( + Varlink *v, + const char *method, + JsonVariant **ret_parameters, + const char **ret_error_id, + VarlinkReplyFlags *ret_flags, ...) { + + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + + va_start(ap, ret_flags); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_call(v, method, parameters, ret_parameters, ret_error_id, ret_flags); +} + +int varlink_reply(Varlink *v, JsonVariant *parameters) { + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, + VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE, + VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE)) + return -EBUSY; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + if (IN_SET(v->state, VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE)) { + /* We just replied to a method call that was let hanging for a while (i.e. we were outside of + * the varlink_dispatch_method() stack frame), which means with this reply we are ready to + * process further messages. */ + v->current = json_variant_unref(v->current); + varlink_set_state(v, VARLINK_IDLE_SERVER); + } else + /* We replied to a method call from within the varlink_dispatch_method() stack frame), which + * means we should it handle the rest of the state engine. */ + varlink_set_state(v, VARLINK_PROCESSED_METHOD); + + return 1; +} + +int varlink_replyb(Varlink *v, ...) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + + va_start(ap, v); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_reply(v, parameters); +} + +int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters) { + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + assert_return(error_id, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, + VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE, + VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE)) + return -EBUSY; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("error", JSON_BUILD_STRING(error_id)), + JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + if (IN_SET(v->state, VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE)) { + v->current = json_variant_unref(v->current); + varlink_set_state(v, VARLINK_IDLE_SERVER); + } else + varlink_set_state(v, VARLINK_PROCESSED_METHOD); + + return 1; +} + +int varlink_errorb(Varlink *v, const char *error_id, ...) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + assert_return(error_id, -EINVAL); + + va_start(ap, error_id); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_error(v, error_id, parameters); +} + +int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) { + + assert_return(v, -EINVAL); + assert_return(parameters, -EINVAL); + + /* We expect to be called in one of two ways: the 'parameters' argument is a string variant in which + * case it is the parameter key name that is invalid. Or the 'parameters' argument is an object + * variant in which case we'll pull out the first key. The latter mode is useful in functions that + * don't expect any arguments. */ + + if (json_variant_is_string(parameters)) + return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, parameters); + + if (json_variant_is_object(parameters) && + json_variant_elements(parameters) > 0) + return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, + json_variant_by_index(parameters, 0)); + + return -EINVAL; +} + +int varlink_notify(Varlink *v, JsonVariant *parameters) { + _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; + int r; + + assert_return(v, -EINVAL); + + if (v->state == VARLINK_DISCONNECTED) + return -ENOTCONN; + if (!IN_SET(v->state, VARLINK_PROCESSING_METHOD_MORE, VARLINK_PENDING_METHOD_MORE)) + return -EBUSY; + + r = varlink_sanitize_parameters(¶meters); + if (r < 0) + return r; + + r = json_build(&m, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("parameters", JSON_BUILD_VARIANT(parameters)), + JSON_BUILD_PAIR("continues", JSON_BUILD_BOOLEAN(true)))); + if (r < 0) + return r; + + r = varlink_enqueue_json(v, m); + if (r < 0) + return r; + + /* No state change, as more is coming */ + return 1; +} + +int varlink_notifyb(Varlink *v, ...) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL; + va_list ap; + int r; + + assert_return(v, -EINVAL); + + va_start(ap, v); + r = json_buildv(¶meters, ap); + va_end(ap); + + if (r < 0) + return r; + + return varlink_notify(v, parameters); +} + +int varlink_bind_reply(Varlink *v, VarlinkReply callback) { + assert_return(v, -EINVAL); + + if (callback && v->reply_callback && callback != v->reply_callback) + return -EBUSY; + + v->reply_callback = callback; + + return 0; +} + +void* varlink_set_userdata(Varlink *v, void *userdata) { + void *old; + + assert_return(v, NULL); + + old = v->userdata; + v->userdata = userdata; + + return old; +} + +void* varlink_get_userdata(Varlink *v) { + assert_return(v, NULL); + + return v->userdata; +} + +static int varlink_acquire_ucred(Varlink *v) { + int r; + + assert(v); + + if (v->ucred_acquired) + return 0; + + r = getpeercred(v->fd, &v->ucred); + if (r < 0) + return r; + + v->ucred_acquired = true; + return 0; +} + +int varlink_get_peer_uid(Varlink *v, uid_t *ret) { + int r; + + assert_return(v, -EINVAL); + assert_return(ret, -EINVAL); + + r = varlink_acquire_ucred(v); + if (r < 0) + return r; + + if (!uid_is_valid(v->ucred.uid)) + return -ENODATA; + + *ret = v->ucred.uid; + return 0; +} + +int varlink_get_peer_pid(Varlink *v, pid_t *ret) { + int r; + + assert_return(v, -EINVAL); + assert_return(ret, -EINVAL); + + r = varlink_acquire_ucred(v); + if (r < 0) + return r; + + if (!pid_is_valid(v->ucred.pid)) + return -ENODATA; + + *ret = v->ucred.pid; + return 0; +} + +int varlink_set_relative_timeout(Varlink *v, usec_t timeout) { + assert_return(v, -EINVAL); + assert_return(timeout > 0, -EINVAL); + + v->timeout = timeout; + return 0; +} + +VarlinkServer *varlink_get_server(Varlink *v) { + assert_return(v, NULL); + + return v->server; +} + +int varlink_set_description(Varlink *v, const char *description) { + assert_return(v, -EINVAL); + + return free_and_strdup(&v->description, description); +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Varlink *v = userdata; + + assert(s); + assert(v); + + handle_revents(v, revents); + (void) varlink_process(v); + + return 1; +} + +static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { + Varlink *v = userdata; + + assert(s); + assert(v); + + (void) varlink_process(v); + return 1; +} + +static int defer_callback(sd_event_source *s, void *userdata) { + Varlink *v = userdata; + + assert(s); + assert(v); + + (void) varlink_process(v); + return 1; +} + +static int prepare_callback(sd_event_source *s, void *userdata) { + Varlink *v = userdata; + int r, e; + usec_t until; + + assert(s); + assert(v); + + e = varlink_get_events(v); + if (e < 0) + return e; + + r = sd_event_source_set_io_events(v->io_event_source, e); + if (r < 0) + return r; + + r = varlink_get_timeout(v, &until); + if (r < 0) + return r; + if (r > 0) { + r = sd_event_source_set_time(v->time_event_source, until); + if (r < 0) + return r; + } + + r = sd_event_source_set_enabled(v->time_event_source, r > 0 ? SD_EVENT_ON : SD_EVENT_OFF); + if (r < 0) + return r; + + return 1; +} + +static int quit_callback(sd_event_source *event, void *userdata) { + Varlink *v = userdata; + + assert(event); + assert(v); + + varlink_flush(v); + varlink_close(v); + + return 1; +} + +int varlink_attach_event(Varlink *v, sd_event *e, int64_t priority) { + int r; + + assert_return(v, -EINVAL); + assert_return(!v->event, -EBUSY); + + if (e) + v->event = sd_event_ref(e); + else { + r = sd_event_default(&v->event); + if (r < 0) + return r; + } + + r = sd_event_add_time(v->event, &v->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, v); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(v->time_event_source, priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(v->time_event_source, "varlink-time"); + + r = sd_event_add_exit(v->event, &v->quit_event_source, quit_callback, v); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(v->quit_event_source, priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(v->quit_event_source, "varlink-quit"); + + r = sd_event_add_io(v->event, &v->io_event_source, v->fd, 0, io_callback, v); + if (r < 0) + goto fail; + + r = sd_event_source_set_prepare(v->io_event_source, prepare_callback); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(v->io_event_source, priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(v->io_event_source, "varlink-io"); + + r = sd_event_add_defer(v->event, &v->defer_event_source, defer_callback, v); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(v->defer_event_source, priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(v->defer_event_source, "varlink-defer"); + + return 0; + +fail: + varlink_detach_event(v); + return r; +} + + +void varlink_detach_event(Varlink *v) { + if (!v) + return; + + varlink_detach_event_sources(v); + + v->event = sd_event_unref(v->event); +} + +sd_event *varlink_get_event(Varlink *v) { + assert_return(v, NULL); + + return v->event; +} + +int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags) { + VarlinkServer *s; + + assert_return(ret, -EINVAL); + assert_return((flags & ~_VARLINK_SERVER_FLAGS_ALL) == 0, -EINVAL); + + s = new(VarlinkServer, 1); + if (!s) + return -ENOMEM; + + *s = (VarlinkServer) { + .n_ref = 1, + .flags = flags, + .connections_max = varlink_server_connections_max(NULL), + .connections_per_uid_max = varlink_server_connections_per_uid_max(NULL), + }; + + *ret = s; + return 0; +} + +static VarlinkServer* varlink_server_destroy(VarlinkServer *s) { + char *m; + + if (!s) + return NULL; + + varlink_server_shutdown(s); + + while ((m = hashmap_steal_first_key(s->methods))) + free(m); + + hashmap_free(s->methods); + hashmap_free(s->by_uid); + + sd_event_unref(s->event); + + free(s->description); + + return mfree(s); +} + +DEFINE_TRIVIAL_REF_UNREF_FUNC(VarlinkServer, varlink_server, varlink_server_destroy); + +static int validate_connection(VarlinkServer *server, const struct ucred *ucred) { + int allowed = -1; + + assert(server); + assert(ucred); + + if (FLAGS_SET(server->flags, VARLINK_SERVER_ROOT_ONLY)) + allowed = ucred->uid == 0; + + if (FLAGS_SET(server->flags, VARLINK_SERVER_MYSELF_ONLY)) + allowed = allowed > 0 || ucred->uid == getuid(); + + if (allowed == 0) { /* Allow access when it is explicitly allowed or when neither + * VARLINK_SERVER_ROOT_ONLY nor VARLINK_SERVER_MYSELF_ONLY are specified. */ + varlink_server_log(server, "Unprivileged client attempted connection, refusing."); + return 0; + } + + if (server->n_connections >= server->connections_max) { + varlink_server_log(server, "Connection limit of %u reached, refusing.", server->connections_max); + return 0; + } + + if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) { + unsigned c; + + if (!uid_is_valid(ucred->uid)) { + varlink_server_log(server, "Client with invalid UID attempted connection, refusing."); + return 0; + } + + c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid))); + if (c >= server->connections_per_uid_max) { + varlink_server_log(server, "Per-UID connection limit of %u reached, refusing.", + server->connections_per_uid_max); + return 0; + } + } + + return 1; +} + +static int count_connection(VarlinkServer *server, struct ucred *ucred) { + unsigned c; + int r; + + assert(server); + assert(ucred); + + server->n_connections++; + + if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) { + r = hashmap_ensure_allocated(&server->by_uid, NULL); + if (r < 0) + return log_debug_errno(r, "Failed to allocate UID hash table: %m"); + + c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid))); + + varlink_server_log(server, "Connections of user " UID_FMT ": %u (of %u max)", + ucred->uid, c, server->connections_per_uid_max); + + r = hashmap_replace(server->by_uid, UID_TO_PTR(ucred->uid), UINT_TO_PTR(c + 1)); + if (r < 0) + return log_debug_errno(r, "Failed to increment counter in UID hash table: %m"); + } + + return 0; +} + +int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret) { + _cleanup_(varlink_unrefp) Varlink *v = NULL; + bool ucred_acquired; + struct ucred ucred; + int r; + + assert_return(server, -EINVAL); + assert_return(fd >= 0, -EBADF); + + if ((server->flags & (VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_ACCOUNT_UID)) != 0) { + r = getpeercred(fd, &ucred); + if (r < 0) + return varlink_server_log_errno(server, r, "Failed to acquire peer credentials of incoming socket, refusing: %m"); + + ucred_acquired = true; + + r = validate_connection(server, &ucred); + if (r < 0) + return r; + if (r == 0) + return -EPERM; + } else + ucred_acquired = false; + + r = varlink_new(&v); + if (r < 0) + return varlink_server_log_errno(server, r, "Failed to allocate connection object: %m"); + + r = count_connection(server, &ucred); + if (r < 0) + return r; + + v->fd = fd; + v->userdata = server->userdata; + if (ucred_acquired) { + v->ucred = ucred; + v->ucred_acquired = true; + } + + (void) asprintf(&v->description, "%s-%i", server->description ?: "varlink", v->fd); + + /* Link up the server and the connection, and take reference in both directions. Note that the + * reference on the connection is left dangling. It will be dropped when the connection is closed, + * which happens in varlink_close(), including in the event loop quit callback. */ + v->server = varlink_server_ref(server); + varlink_ref(v); + + varlink_set_state(v, VARLINK_IDLE_SERVER); + + r = varlink_attach_event(v, server->event, server->event_priority); + if (r < 0) { + varlink_log_errno(v, r, "Failed to attach new connection: %m"); + v->fd = -1; /* take the fd out of the connection again */ + varlink_close(v); + return r; + } + + if (ret) + *ret = v; + + return 0; +} + +static int connect_callback(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + VarlinkServerSocket *ss = userdata; + _cleanup_close_ int cfd = -1; + Varlink *v = NULL; + int r; + + assert(source); + assert(ss); + + varlink_server_log(ss->server, "New incoming connection."); + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + return 0; + + return varlink_server_log_errno(ss->server, errno, "Failed to accept incoming socket: %m"); + } + + r = varlink_server_add_connection(ss->server, cfd, &v); + if (r < 0) + return 0; + + TAKE_FD(cfd); + + if (ss->server->connect_callback) { + r = ss->server->connect_callback(ss->server, v, ss->server->userdata); + if (r < 0) { + varlink_log_errno(v, r, "Connection callback returned error, disconnecting client: %m"); + varlink_close(v); + return 0; + } + } + + return 0; +} + +int varlink_server_listen_fd(VarlinkServer *s, int fd) { + _cleanup_free_ VarlinkServerSocket *ss = NULL; + int r; + + assert_return(s, -EINVAL); + assert_return(fd >= 0, -EBADF); + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + ss = new(VarlinkServerSocket, 1); + if (!ss) + return -ENOMEM; + + *ss = (VarlinkServerSocket) { + .server = s, + .fd = fd, + }; + + if (s->event) { + _cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL; + + r = sd_event_add_io(s->event, &es, fd, EPOLLIN, connect_callback, ss); + if (r < 0) + return r; + + r = sd_event_source_set_priority(ss->event_source, s->event_priority); + if (r < 0) + return r; + } + + LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss)); + return 0; +} + +int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t m) { + union sockaddr_union sockaddr; + _cleanup_close_ int fd = -1; + int r; + + assert_return(s, -EINVAL); + assert_return(address, -EINVAL); + assert_return((m & ~0777) == 0, -EINVAL); + + r = sockaddr_un_set_path(&sockaddr.un, address); + if (r < 0) + return r; + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + (void) sockaddr_un_unlink(&sockaddr.un); + + RUN_WITH_UMASK(~m & 0777) + if (bind(fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) + return -errno; + + if (listen(fd, SOMAXCONN) < 0) + return -errno; + + r = varlink_server_listen_fd(s, fd); + if (r < 0) + return r; + + TAKE_FD(fd); + return 0; +} + +void* varlink_server_set_userdata(VarlinkServer *s, void *userdata) { + void *ret; + + assert_return(s, NULL); + + ret = s->userdata; + s->userdata = userdata; + + return ret; +} + +void* varlink_server_get_userdata(VarlinkServer *s) { + assert_return(s, NULL); + + return s->userdata; +} + +static VarlinkServerSocket* varlink_server_socket_destroy(VarlinkServerSocket *ss) { + if (!ss) + return NULL; + + if (ss->server) + LIST_REMOVE(sockets, ss->server->sockets, ss); + + sd_event_source_disable_unref(ss->event_source); + + free(ss->address); + safe_close(ss->fd); + + return mfree(ss); +} + +int varlink_server_shutdown(VarlinkServer *s) { + assert_return(s, -EINVAL); + + while (s->sockets) + varlink_server_socket_destroy(s->sockets); + + return 0; +} + +int varlink_server_attach_event(VarlinkServer *s, sd_event *e, int64_t priority) { + VarlinkServerSocket *ss; + int r; + + assert_return(s, -EINVAL); + assert_return(!s->event, -EBUSY); + + if (e) + s->event = sd_event_ref(e); + else { + r = sd_event_default(&s->event); + if (r < 0) + return r; + } + + LIST_FOREACH(sockets, ss, s->sockets) { + assert(!ss->event_source); + + r = sd_event_add_io(s->event, &ss->event_source, ss->fd, EPOLLIN, connect_callback, ss); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(ss->event_source, priority); + if (r < 0) + goto fail; + } + + s->event_priority = priority; + return 0; + +fail: + varlink_server_detach_event(s); + return r; +} + +int varlink_server_detach_event(VarlinkServer *s) { + VarlinkServerSocket *ss; + + assert_return(s, -EINVAL); + + LIST_FOREACH(sockets, ss, s->sockets) { + + if (!ss->event_source) + continue; + + (void) sd_event_source_set_enabled(ss->event_source, SD_EVENT_OFF); + ss->event_source = sd_event_source_unref(ss->event_source); + } + + sd_event_unref(s->event); + return 0; +} + +sd_event *varlink_server_get_event(VarlinkServer *s) { + assert_return(s, NULL); + + return s->event; +} + +int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMethod callback) { + char *m; + int r; + + assert_return(s, -EINVAL); + assert_return(method, -EINVAL); + assert_return(callback, -EINVAL); + + if (startswith(method, "org.varlink.service.")) + return -EEXIST; + + r = hashmap_ensure_allocated(&s->methods, &string_hash_ops); + if (r < 0) + return r; + + m = strdup(method); + if (!m) + return -ENOMEM; + + r = hashmap_put(s->methods, m, callback); + if (r < 0) { + free(m); + return r; + } + + return 0; +} + +int varlink_server_bind_method_many_internal(VarlinkServer *s, ...) { + va_list ap; + int r; + + assert_return(s, -EINVAL); + + va_start(ap, s); + for (;;) { + VarlinkMethod callback; + const char *method; + + method = va_arg(ap, const char *); + if (!method) + break; + + callback = va_arg(ap, VarlinkMethod); + + r = varlink_server_bind_method(s, method, callback); + if (r < 0) + return r; + } + + return 0; +} + +int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) { + assert_return(s, -EINVAL); + + if (callback && s->connect_callback && callback != s->connect_callback) + return -EBUSY; + + s->connect_callback = callback; + return 0; +} + +unsigned varlink_server_connections_max(VarlinkServer *s) { + struct rlimit rl; + + /* If a server is specified, return the setting for that server, otherwise the default value */ + if (s) + return s->connections_max; + + assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); + + /* Make sure we never use up more than ¾th of RLIMIT_NOFILE for IPC */ + if (VARLINK_DEFAULT_CONNECTIONS_MAX > rl.rlim_cur / 4 * 3) + return rl.rlim_cur / 4 * 3; + + return VARLINK_DEFAULT_CONNECTIONS_MAX; +} + +unsigned varlink_server_connections_per_uid_max(VarlinkServer *s) { + unsigned m; + + if (s) + return s->connections_per_uid_max; + + /* Make sure to never use up more than ¾th of available connections for a single user */ + m = varlink_server_connections_max(NULL); + if (VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX > m) + return m / 4 * 3; + + return VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX; +} + +int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m) { + assert_return(s, -EINVAL); + assert_return(m > 0, -EINVAL); + + s->connections_per_uid_max = m; + return 0; +} + +int varlink_server_set_connections_max(VarlinkServer *s, unsigned m) { + assert_return(s, -EINVAL); + assert_return(m > 0, -EINVAL); + + s->connections_max = m; + return 0; +} + +int varlink_server_set_description(VarlinkServer *s, const char *description) { + assert_return(s, -EINVAL); + + return free_and_strdup(&s->description, description); +} diff --git a/src/shared/varlink.h b/src/shared/varlink.h new file mode 100644 index 0000000000..d96fa93619 --- /dev/null +++ b/src/shared/varlink.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-event.h" + +#include "json.h" +#include "time-util.h" + +/* A minimal Varlink implementation. We only implement the minimal, obvious bits here though. No validation, + * no introspection, no name service, just the stuff actually needed. + * + * You might wonder why we aren't using libvarlink here? Varlink is a very simple protocol, which allows us + * to write our own implementation relatively easily. However, the main reasons are these: + * + * • We want to use our own JSON subsystem, with all the benefits that brings (i.e. accurate unsigned+signed + * 64bit integers, full fuzzing, logging during parsing and so on). If we'd want to use that with + * libvarlink we'd have to serialize and deserialize all the time from its own representation which is + * inefficient and nasty. + * + * • We want integration into sd-event, but also synchronous event-loop-less operation + * + * • We need proper per-UID accounting and access control, since we want to allow communication between + * unprivileged clients and privileged servers. + * + * • And of course, we don't want the name service and introspection stuff for now (though that might + * change). + */ + +typedef struct Varlink Varlink; +typedef struct VarlinkServer VarlinkServer; + +typedef enum VarlinkReplyFlags { + VARLINK_REPLY_ERROR = 1 << 0, + VARLINK_REPLY_CONTINUES = 1 << 1, + VARLINK_REPLY_LOCAL = 1 << 2, +} VarlinkReplyFlags; + +typedef enum VarlinkMethodFlags { + VARLINK_METHOD_ONEWAY = 1 << 0, + VARLINK_METHOD_MORE = 2 << 1, +} VarlinkMethodFlags; + +typedef enum VarlinkServerFlags { + VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */ + VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */ + VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */ + + _VARLINK_SERVER_FLAGS_ALL = (1 << 3) - 1, +} VarlinkServerFlags; + +typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata); +typedef int (*VarlinkReply)(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata); +typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userdata); + +int varlink_connect_address(Varlink **ret, const char *address); +int varlink_connect_fd(Varlink **ret, int fd); + +Varlink* varlink_ref(Varlink *link); +Varlink* varlink_unref(Varlink *v); + +int varlink_get_fd(Varlink *v); +int varlink_get_events(Varlink *v); +int varlink_get_timeout(Varlink *v, usec_t *ret); + +int varlink_attach_event(Varlink *v, sd_event *e, int64_t priority); +void varlink_detach_event(Varlink *v); +sd_event *varlink_get_event(Varlink *v); + +int varlink_process(Varlink *v); +int varlink_wait(Varlink *v, usec_t timeout); + +int varlink_flush(Varlink *v); +int varlink_close(Varlink *v); + +Varlink* varlink_flush_close_unref(Varlink *v); + +/* Enqueue method call, not expecting a reply */ +int varlink_send(Varlink *v, const char *method, JsonVariant *parameters); +int varlink_sendb(Varlink *v, const char *method, ...); + +/* Send method call and wait for reply */ +int varlink_call(Varlink *v, const char *method, JsonVariant *parameters, JsonVariant **ret_parameters, const char **ret_error_id, VarlinkReplyFlags *ret_flags); +int varlink_callb(Varlink *v, const char *method, JsonVariant **ret_parameters, const char **ret_error_id, VarlinkReplyFlags *ret_flags, ...); + +/* Enqueue method call, expect a reply, which is eventually delivered to the reply callback */ +int varlink_invoke(Varlink *v, const char *method, JsonVariant *parameters); +int varlink_invokeb(Varlink *v, const char *method, ...); + +/* Enqueue a final reply */ +int varlink_reply(Varlink *v, JsonVariant *parameters); +int varlink_replyb(Varlink *v, ...); + +/* Enqueue a (final) error */ +int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters); +int varlink_errorb(Varlink *v, const char *error_id, ...); +int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters); + +/* Enqueue a "more" reply */ +int varlink_notify(Varlink *v, JsonVariant *parameters); +int varlink_notifyb(Varlink *v, ...); + +/* Bind a disconnect, reply or timeout callback */ +int varlink_bind_reply(Varlink *v, VarlinkReply reply); + +void* varlink_set_userdata(Varlink *v, void *userdata); +void* varlink_get_userdata(Varlink *v); + +int varlink_get_peer_uid(Varlink *v, uid_t *ret); +int varlink_get_peer_pid(Varlink *v, pid_t *ret); + +int varlink_set_relative_timeout(Varlink *v, usec_t usec); + +VarlinkServer* varlink_get_server(Varlink *v); + +int varlink_set_description(Varlink *v, const char *d); + +/* Create a varlink server */ +int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags); +VarlinkServer *varlink_server_ref(VarlinkServer *s); +VarlinkServer *varlink_server_unref(VarlinkServer *s); + +/* Add addresses or fds to listen on */ +int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t mode); +int varlink_server_listen_fd(VarlinkServer *s, int fd); +int varlink_server_add_connection(VarlinkServer *s, int fd, Varlink **ret); + +/* Bind callbacks */ +int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMethod callback); +int varlink_server_bind_method_many_internal(VarlinkServer *s, ...); +#define varlink_server_bind_method_many(s, ...) varlink_server_bind_method_many_internal(s, __VA_ARGS__, NULL) +int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect); + +void* varlink_server_set_userdata(VarlinkServer *s, void *userdata); +void* varlink_server_get_userdata(VarlinkServer *s); + +int varlink_server_attach_event(VarlinkServer *v, sd_event *e, int64_t priority); +int varlink_server_detach_event(VarlinkServer *v); +sd_event *varlink_server_get_event(VarlinkServer *v); + +int varlink_server_shutdown(VarlinkServer *server); + +unsigned varlink_server_connections_max(VarlinkServer *s); +unsigned varlink_server_connections_per_uid_max(VarlinkServer *s); + +int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m); +int varlink_server_set_connections_max(VarlinkServer *s, unsigned m); + +int varlink_server_set_description(VarlinkServer *s, const char *description); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_flush_close_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref); + +#define VARLINK_ERROR_DISCONNECTED "io.systemd.Disconnected" +#define VARLINK_ERROR_TIMEOUT "io.systemd.TimedOut" +#define VARLINK_ERROR_PROTOCOL "io.systemd.Protocol" +#define VARLINK_ERROR_SYSTEM "io.systemd.System" + +#define VARLINK_ERROR_INTERFACE_NOT_FOUND "org.varlink.service.InterfaceNotFound" +#define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound" +#define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented" +#define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter" diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 25a538df50..35b2c2aa46 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -219,7 +219,7 @@ static void sync_with_progress(void) { BLOCK_SIGNALS(SIGCHLD); - /* Due to the possiblity of the sync operation hanging, we fork a child process and monitor the progress. If + /* Due to the possibility of the sync operation hanging, we fork a child process and monitor the progress. If * the timeout lapses, the assumption is that that particular sync stalled. */ r = asynchronous_sync(&pid); diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c index 6afa512bff..c75be39fef 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -55,18 +55,13 @@ void mount_points_list_free(MountPoint **head) { } int mount_points_list_get(const char *mountinfo, MountPoint **head) { - _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; - _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; int r; assert(head); - t = mnt_new_table(); - i = mnt_new_iter(MNT_ITER_FORWARD); - if (!t || !i) - return log_oom(); - - r = mnt_table_parse_mtab(t, mountinfo); + r = libmount_parse(mountinfo, NULL, &table, &iter); if (r < 0) return log_error_errno(r, "Failed to parse %s: %m", mountinfo); @@ -79,7 +74,7 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { bool try_remount_ro; _cleanup_free_ MountPoint *m = NULL; - r = mnt_table_next_fs(t, i, &fs); + r = mnt_table_next_fs(table, iter, &fs); if (r == 1) break; if (r < 0) @@ -390,7 +385,7 @@ static int remount_with_timeout(MountPoint *m, int umount_log_level) { assert(m); - /* Due to the possiblity of a remount operation hanging, we + /* Due to the possibility of a remount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular remount * failed. */ @@ -428,7 +423,7 @@ static int umount_with_timeout(MountPoint *m, int umount_log_level) { assert(m); - /* Due to the possiblity of a umount operation hanging, we + /* Due to the possibility of a umount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular umount * failed. */ @@ -486,7 +481,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount * underlying mount. There's nothing we can do * about it for the general case, but we can * do something about it if it is aliased - * somehwere else via a bind mount. If we + * somewhere else via a bind mount. If we * explicitly remount the super block of that * alias read-only we hence should be * relatively safe regarding keeping a dirty fs diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 11aabaf923..ed570deaee 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -50,7 +50,7 @@ static int write_hibernate_location_info(void) { if (streq(type, "partition")) { r = write_string_file("/sys/power/resume", device, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_debug_errno(r, "Faileed to write partitoin device to /sys/power/resume: %m"); + return log_debug_errno(r, "Failed to write partition device to /sys/power/resume: %m"); return r; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index c9edefdd82..059f7220b5 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -180,7 +180,7 @@ typedef enum BusFocus { _BUS_FOCUS_MAX } BusFocus; -static sd_bus *busses[_BUS_FOCUS_MAX] = {}; +static sd_bus *buses[_BUS_FOCUS_MAX] = {}; static UnitFileFlags args_to_flags(void) { return (arg_runtime ? UNIT_FILE_RUNTIME : 0) | @@ -200,22 +200,22 @@ static int acquire_bus(BusFocus focus, sd_bus **ret) { if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0) focus = BUS_FULL; - if (!busses[focus]) { + if (!buses[focus]) { bool user; user = arg_scope != UNIT_FILE_SYSTEM; if (focus == BUS_MANAGER) - r = bus_connect_transport_systemd(arg_transport, arg_host, user, &busses[focus]); + r = bus_connect_transport_systemd(arg_transport, arg_host, user, &buses[focus]); else - r = bus_connect_transport(arg_transport, arg_host, user, &busses[focus]); + r = bus_connect_transport(arg_transport, arg_host, user, &buses[focus]); if (r < 0) return log_error_errno(r, "Failed to connect to bus: %m"); - (void) sd_bus_set_allow_interactive_authorization(busses[focus], arg_ask_password); + (void) sd_bus_set_allow_interactive_authorization(buses[focus], arg_ask_password); } - *ret = busses[focus]; + *ret = buses[focus]; return 0; } @@ -223,7 +223,7 @@ static void release_busses(void) { BusFocus w; for (w = 0; w < _BUS_FOCUS_MAX; w++) - busses[w] = sd_bus_flush_close_unref(busses[w]); + buses[w] = sd_bus_flush_close_unref(buses[w]); } static void ask_password_agent_open_if_enabled(void) { @@ -4125,10 +4125,12 @@ typedef struct UnitStatusInfo { uint64_t cpu_usage_nsec; uint64_t tasks_current; uint64_t tasks_max; - uint64_t ip_ingress_bytes; uint64_t ip_egress_bytes; + uint64_t io_read_bytes; + uint64_t io_write_bytes; + uint64_t default_memory_min; uint64_t default_memory_low; LIST_HEAD(ExecStatusInfo, exec); @@ -4484,6 +4486,14 @@ static void print_status_info( format_bytes(buf_out, sizeof(buf_out), i->ip_egress_bytes)); } + if (i->io_read_bytes != UINT64_MAX && i->io_write_bytes != UINT64_MAX) { + char buf_in[FORMAT_BYTES_MAX], buf_out[FORMAT_BYTES_MAX]; + + printf(" IO: %s read, %s written\n", + format_bytes(buf_in, sizeof(buf_in), i->io_read_bytes), + format_bytes(buf_out, sizeof(buf_out), i->io_write_bytes)); + } + if (i->tasks_current != (uint64_t) -1) { printf(" Tasks: %" PRIu64, i->tasks_current); @@ -5481,6 +5491,7 @@ static int show_one( { "Where", "s", NULL, offsetof(UnitStatusInfo, where) }, { "What", "s", NULL, offsetof(UnitStatusInfo, what) }, { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) }, + { "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) }, { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) }, { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) }, { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) }, @@ -5493,6 +5504,8 @@ static int show_one( { "TasksMax", "t", NULL, offsetof(UnitStatusInfo, tasks_max) }, { "IPIngressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_ingress_bytes) }, { "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) }, + { "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) }, + { "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) }, { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 }, { "ExecStart", "a(sasbttttuii)", map_exec, 0 }, { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 }, @@ -5517,6 +5530,8 @@ static int show_one( .tasks_max = (uint64_t) -1, .ip_ingress_bytes = (uint64_t) -1, .ip_egress_bytes = (uint64_t) -1, + .io_read_bytes = UINT64_MAX, + .io_write_bytes = UINT64_MAX, }; char **pp; int r; @@ -8803,7 +8818,7 @@ static int systemctl_main(int argc, char *argv[]) { { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatbility with old systemctl <= 228 */ + { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with old systemctl <= 228 */ { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */ { "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */ diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index e3804e203c..0f43554d82 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -54,11 +54,11 @@ enum { extern const unsigned sd_bus_object_vtable_format; -/* Note: unused areas in the sd_bus_vtable[] array must be initalized to 0. The stucture contains an embedded - * union, and the compiler is NOT required to initalize the unused areas of the union when the rest of the - * structure is initalized. Normally the array is defined as read-only data, in which case the linker places - * it in the BSS section, which is always fully initalized, so this is not a concern. But if the array is - * created on the stack or on the heap, care must be taken to initalize the unused areas, for examply by +/* Note: unused areas in the sd_bus_vtable[] array must be initialized to 0. The structure contains an embedded + * union, and the compiler is NOT required to initialize the unused areas of the union when the rest of the + * structure is initialized. Normally the array is defined as read-only data, in which case the linker places + * it in the BSS section, which is always fully initialized, so this is not a concern. But if the array is + * created on the stack or on the heap, care must be taken to initialize the unused areas, for examply by * first memsetting the whole region to zero before filling the data in. */ struct sd_bus_vtable { diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index bd0d429df6..ab62368e9c 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -38,6 +38,7 @@ enum { SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, SD_DHCP_CLIENT_EVENT_EXPIRED = 3, SD_DHCP_CLIENT_EVENT_RENEW = 4, + SD_DHCP_CLIENT_EVENT_SELECTING = 5, }; enum { @@ -98,7 +99,7 @@ enum { typedef struct sd_dhcp_client sd_dhcp_client; -typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); +typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); int sd_dhcp_client_set_callback( sd_dhcp_client *client, sd_dhcp_client_callback_t cb, @@ -154,6 +155,9 @@ int sd_dhcp_client_get_client_id( int sd_dhcp_client_set_mtu( sd_dhcp_client *client, uint32_t mtu); +int sd_dhcp_client_set_max_attempts( + sd_dhcp_client *client, + uint64_t attempt); int sd_dhcp_client_set_client_port( sd_dhcp_client *client, uint16_t port); @@ -172,6 +176,7 @@ int sd_dhcp_client_get_lease( int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); +int sd_dhcp_client_send_release(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 7bb8609376..b14c92697b 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -113,6 +113,7 @@ int sd_event_get_iteration(sd_event *e, uint64_t *ret); sd_event_source* sd_event_source_ref(sd_event_source *s); sd_event_source* sd_event_source_unref(sd_event_source *s); +sd_event_source* sd_event_source_disable_unref(sd_event_source *s); sd_event *sd_event_source_get_event(sd_event_source *s); void* sd_event_source_get_userdata(sd_event_source *s); @@ -149,6 +150,7 @@ int sd_event_source_set_floating(sd_event_source *s, int b); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_disable_unref); _SD_END_DECLARATIONS; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index d327b27308..d27e0ad201 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -18,11 +18,11 @@ ***/ #include <inttypes.h> -#include <linux/neighbour.h> -#include <linux/rtnetlink.h> #include <net/ethernet.h> #include <netinet/ether.h> #include <netinet/in.h> +#include <linux/neighbour.h> +#include <linux/rtnetlink.h> #include "sd-event.h" diff --git a/src/test/meson.build b/src/test/meson.build index e58e1cc73d..ee6cdb6d54 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -269,6 +269,10 @@ tests += [ [], []], + [['src/test/test-umask-util.c'], + [], + []], + [['src/test/test-proc-cmdline.c'], [], []], @@ -591,6 +595,10 @@ tests += [ libmount, libblkid]], + [['src/test/test-varlink.c'], + [], + [threads]], + [['src/test/test-cgroup-util.c'], [], []], @@ -983,7 +991,10 @@ tests += [ ] -if cxx_cmd != '' +# test-bus-vtable-cc.cc is a symlink and symlinks get lost in containers on FuzzBuzz. +# The issue has been reported to the developers of FuzzBuzz and hopefully will be fixed soon. +# In the meantime, let's just skip the symlink there. +if cxx_cmd != '' and not want_fuzzbuzz tests += [ [['src/libsystemd/sd-bus/test-bus-vtable-cc.cc'], [], diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c index ad10eb178a..e6b6d96d5a 100644 --- a/src/test/test-alloc-util.c +++ b/src/test/test-alloc-util.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <malloc.h> #include <stdint.h> #include "alloc-util.h" #include "macro.h" #include "memory-util.h" +#include "tests.h" static void test_alloca(void) { static const uint8_t zero[997] = { }; @@ -21,21 +23,35 @@ static void test_alloca(void) { static void test_GREEDY_REALLOC(void) { _cleanup_free_ int *a = NULL, *b = NULL; - size_t n_allocated = 0, i; + size_t n_allocated = 0, i, j; - /* Give valgrind a chance to verify our realloc operations */ + /* Give valgrind a chance to verify our realloc() operations */ - for (i = 0; i < 2048; i++) { + for (i = 0; i < 20480; i++) { assert_se(GREEDY_REALLOC(a, n_allocated, i + 1)); - a[i] = i; + assert_se(n_allocated >= i + 1); + assert_se(malloc_usable_size(a) >= (i + 1) * sizeof(int)); + a[i] = (int) i; assert_se(GREEDY_REALLOC(a, n_allocated, i / 2)); + assert_se(n_allocated >= i / 2); + assert_se(malloc_usable_size(a) >= (i / 2) * sizeof(int)); } - for (i = 30, n_allocated = 0; i < 2048; i+=7) { + for (j = 0; j < i / 2; j++) + assert_se(a[j] == (int) j); + + for (i = 30, n_allocated = 0; i < 20480; i += 7) { assert_se(GREEDY_REALLOC(b, n_allocated, i + 1)); - b[i] = i; + assert_se(n_allocated >= i + 1); + assert_se(malloc_usable_size(b) >= (i + 1) * sizeof(int)); + b[i] = (int) i; assert_se(GREEDY_REALLOC(b, n_allocated, i / 2)); + assert_se(n_allocated >= i / 2); + assert_se(malloc_usable_size(b) >= (i / 2) * sizeof(int)); } + + for (j = 30; j < i / 2; j += 7) + assert_se(b[j] == (int) j); } static void test_memdup_multiply_and_greedy_realloc(void) { @@ -91,11 +107,39 @@ static void test_bool_assign(void) { assert(!h); } +static int cleanup_counter = 0; + +static void cleanup1(void *a) { + log_info("%s(%p)", __func__, a); + assert_se(++cleanup_counter == *(int*) a); +} +static void cleanup2(void *a) { + log_info("%s(%p)", __func__, a); + assert_se(++cleanup_counter == *(int*) a); +} +static void cleanup3(void *a) { + log_info("%s(%p)", __func__, a); + assert_se(++cleanup_counter == *(int*) a); +} + +static void test_cleanup_order(void) { + _cleanup_(cleanup1) int x1 = 4, x2 = 3; + _cleanup_(cleanup3) int z = 2; + _cleanup_(cleanup2) int y = 1; + log_debug("x1: %p", &x1); + log_debug("x2: %p", &x2); + log_debug("y: %p", &y); + log_debug("z: %p", &z); +} + int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + test_alloca(); test_GREEDY_REALLOC(); test_memdup_multiply_and_greedy_realloc(); test_bool_assign(); + test_cleanup_order(); return 0; } diff --git a/src/test/test-bpf.c b/src/test/test-bpf.c index 6a0bf1462f..b09e8c714c 100644 --- a/src/test/test-bpf.c +++ b/src/test/test-bpf.c @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) { p = bpf_program_unref(p); - /* The simple tests suceeded. Now let's try full unit-based use-case. */ + /* The simple tests succeeded. Now let's try full unit-based use-case. */ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL) >= 0); diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 6b2de66bb7..f9fae84dde 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -99,7 +99,7 @@ static int setup_tests(bool *run_ambient) { nobody = getpwnam(NOBODY_USER_NAME); if (!nobody) - return log_error_errno(errno, "Could not find nobody user: %m"); + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Could not find nobody user: %m"); test_uid = nobody->pw_uid; test_gid = nobody->pw_gid; diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c index 7372f97a1c..4fb629217f 100644 --- a/src/test/test-cgroup-unit-default.c +++ b/src/test/test-cgroup-unit-default.c @@ -39,7 +39,7 @@ static int test_default_memory_low(void) { * 1. dml-passthrough.slice sets MemoryLow=100. This should not affect its children, as only * DefaultMemoryLow is propagated, not MemoryLow. As such, all leaf services should end up with * memory.low as 50, inherited from dml.slice, *except* for dml-passthrough-set-ml.service, which - * should have the value of 25, as it has MemoryLow explicitly set. + * should have the value of 0, as it has MemoryLow explicitly set. * * ┌───────────┠* │ dml.slice │ @@ -49,7 +49,7 @@ static int test_default_memory_low(void) { * │ dml-passthrough.slice │ * └───────────┬───────────┘ * ┌───────────────────────────────────┼───────────────────────────────────┠- * no new settings DefaultMemoryLow=15 MemoryLow=25 + * no new settings DefaultMemoryLow=15 MemoryLow=0 * ┌───────────────┴───────────────┠┌────────────────┴────────────────┠┌───────────────┴────────────────┠* │ dml-passthrough-empty.service │ │ dml-passthrough-set-dml.service │ │ dml-passthrough-set-ml.service │ * └───────────────────────────────┘ └─────────────────────────────────┘ └────────────────────────────────┘ @@ -122,7 +122,7 @@ static int test_default_memory_low(void) { assert_se(unit_get_ancestor_memory_low(dml_passthrough) == 100); assert_se(unit_get_ancestor_memory_low(dml_passthrough_empty) == dml_tree_default); assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_dml) == 50); - assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 25); + assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 0); assert_se(unit_get_ancestor_memory_low(dml_override) == dml_tree_default); assert_se(unit_get_ancestor_memory_low(dml_override_empty) == 10); diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index cd1db3dd62..211def88eb 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -717,7 +717,7 @@ static void test_read_line_one_file(FILE *f) { line = mfree(line); /* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first - * character after the previous limit. Let's make use of tha to continue our test. */ + * character after the previous limit. Let's make use of that to continue our test. */ assert_se(read_line(f, 1024, &line) == 62 && streq(line, "line that is supposed to be truncated, because it is so long")); line = mfree(line); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index cfddfe19a7..323e1124ba 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -112,7 +112,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - /* Disabling a disabled unit must suceed but be a NOP */ + /* Disabling a disabled unit must succeed but be a NOP */ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); diff --git a/src/test/test-libmount.c b/src/test/test-libmount.c index fc28f27d53..c3395493d4 100644 --- a/src/test/test-libmount.c +++ b/src/test/test-libmount.c @@ -21,13 +21,10 @@ static void test_libmount_unescaping_one( _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; _cleanup_fclose_ FILE *f = NULL; - assert_se(table = mnt_new_table()); - assert_se(iter = mnt_new_iter(MNT_ITER_FORWARD)); - f = fmemopen((char*) string, strlen(string), "re"); assert_se(f); - assert_se(mnt_table_parse_stream(table, f, title) >= 0); + assert_se(libmount_parse(title, f, &table, &iter) >= 0); struct libmnt_fs *fs; const char *source, *target; diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index f634ca28db..7878512465 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -158,7 +158,7 @@ static int enumerate_print_list(struct udev_enumerate *enumerate) { device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), udev_list_entry_get_name(list_entry)); - if (device != NULL) { + if (device) { log_info("device: '%s' (%s)", udev_device_get_syspath(device), udev_device_get_subsystem(device)); @@ -249,7 +249,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate '%s'", subsystem == NULL ? "<all>" : subsystem); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_match_subsystem(udev_enumerate, subsystem); udev_enumerate_scan_devices(udev_enumerate); @@ -258,7 +258,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'net' + duplicated scan + null + zero"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_match_subsystem(udev_enumerate, "net"); udev_enumerate_scan_devices(udev_enumerate); @@ -278,7 +278,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'block'"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_match_subsystem(udev_enumerate,"block"); r = udev_enumerate_add_match_is_initialized(udev_enumerate); @@ -292,7 +292,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'not block'"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block"); udev_enumerate_scan_devices(udev_enumerate); @@ -301,7 +301,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'pci, mem, vc'"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_match_subsystem(udev_enumerate, "pci"); udev_enumerate_add_match_subsystem(udev_enumerate, "mem"); @@ -312,7 +312,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'subsystem'"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_scan_subsystems(udev_enumerate); enumerate_print_list(udev_enumerate); @@ -320,7 +320,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { log_info("enumerate 'property IF_FS_*=filesystem'"); udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) + if (!udev_enumerate) return -1; udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem"); udev_enumerate_scan_devices(udev_enumerate); diff --git a/src/test/test-nss.c b/src/test/test-nss.c index 27afe36082..d0f9898378 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -88,7 +88,7 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { if (it->scopeid == 0) goto numerical_index; - if (if_indextoname(it->scopeid, ifname) == NULL) { + if (!if_indextoname(it->scopeid, ifname)) { log_warning_errno(errno, "if_indextoname(%d) failed: %m", it->scopeid); numerical_index: xsprintf(ifname, "%i", it->scopeid); diff --git a/src/test/test-pretty-print.c b/src/test/test-pretty-print.c index 3ad80d145d..9236eb1315 100644 --- a/src/test/test-pretty-print.c +++ b/src/test/test-pretty-print.c @@ -15,7 +15,7 @@ static void test_terminal_urlify(void) { _cleanup_free_ char *formatted = NULL; assert_se(terminal_urlify("https://www.freedesktop.org/wiki/Software/systemd/", "systemd homepage", &formatted) >= 0); - printf("Hey, considere visiting the %s right now! It is very good!\n", formatted); + printf("Hey, consider visiting the %s right now! It is very good!\n", formatted); formatted = mfree(formatted); diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index e0c513ad8c..7e949154b1 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -61,7 +61,7 @@ static void test_sleep(void) { **reboot = strv_new("reboot"), **platform = strv_new("platform"), **shutdown = strv_new("shutdown"), - **freez = strv_new("freeze"); + **freeze = strv_new("freeze"); int r; log_info("/* %s */", __func__); @@ -74,7 +74,7 @@ static void test_sleep(void) { log_info("Hibernate+Reboot configured: %s", yes_no(can_sleep_disk(reboot) > 0)); log_info("Hibernate+Platform configured: %s", yes_no(can_sleep_disk(platform) > 0)); log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0)); - log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0)); + log_info("Freeze configured: %s", yes_no(can_sleep_state(freeze) > 0)); log_info("/= running system =/"); r = can_sleep("suspend"); diff --git a/src/test/test-systemd-tmpfiles.py b/src/test/test-systemd-tmpfiles.py index 83a66e8772..7f4af38940 100755 --- a/src/test/test-systemd-tmpfiles.py +++ b/src/test/test-systemd-tmpfiles.py @@ -52,8 +52,8 @@ def test_invalids(*, user): test_line('w /unresolved/argument/sandwich - - - - "%v%Y%v"', user=user) test_line('w /unresolved/filename/%Y - - - - "whatever"', user=user) test_line('w /unresolved/filename/sandwich/%v%Y%v - - - - "whatever"', user=user) - test_line('w - - - - - "no file specfied"', user=user) - test_line('C - - - - - "no file specfied"', user=user) + test_line('w - - - - - "no file specified"', user=user) + test_line('C - - - - - "no file specified"', user=user) test_line('C non/absolute/path - - - - -', user=user) test_line('b - - - - - -', user=user) test_line('b 1234 - - - - -', user=user) diff --git a/src/test/test-umask-util.c b/src/test/test-umask-util.c new file mode 100644 index 0000000000..27f6b56541 --- /dev/null +++ b/src/test/test-umask-util.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "tests.h" +#include "umask-util.h" + +int main(int argc, char *argv[]) { + size_t n; + mode_t u; + + test_setup_logging(LOG_DEBUG); + + u = umask(0111); + + n = 0; + RUN_WITH_UMASK(0123) { + assert_se(umask(000) == 0123); + n++; + } + + assert_se(n == 1); + assert_se(umask(u) == 0111); + + RUN_WITH_UMASK(0135) { + assert_se(umask(000) == 0135); + n++; + } + + assert_se(n == 2); + assert_se(umask(0111) == u); + + RUN_WITH_UMASK(0315) { + assert_se(umask(000) == 0315); + n++; + break; + } + + assert_se(n == 3); + assert_se(umask(u) == 0111); + + return EXIT_SUCCESS; +} diff --git a/src/test/test-varlink.c b/src/test/test-varlink.c new file mode 100644 index 0000000000..fbfc72cd81 --- /dev/null +++ b/src/test/test-varlink.c @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <fcntl.h> +#include <poll.h> +#include <pthread.h> + +#include "sd-event.h" + +#include "fd-util.h" +#include "json.h" +#include "rm-rf.h" +#include "strv.h" +#include "tmpfile-util.h" +#include "user-util.h" +#include "varlink.h" + +/* Let's pick some high value, that is higher than the largest listen() backlog, but leaves enough room below + the typical RLIMIT_NOFILE value of 1024 so that we can process both sides of each socket in our + process. Or in other words: "OVERLOAD_CONNECTIONS * 2 + x < 1024" should hold, for some small x that + should cover any auxiliary fds, the listener server fds, stdin/stdout/stderr and whatever else. */ +#define OVERLOAD_CONNECTIONS 333 + +static int n_done = 0; +static int block_write_fd = -1; + +static int method_something(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + _cleanup_(json_variant_unrefp) JsonVariant *ret = NULL; + JsonVariant *a, *b; + intmax_t x, y; + int r; + + a = json_variant_by_key(parameters, "a"); + if (!a) + return varlink_error(link, "io.test.BadParameters", NULL); + + x = json_variant_integer(a); + + b = json_variant_by_key(parameters, "b"); + if (!b) + return varlink_error(link, "io.test.BadParameters", NULL); + + y = json_variant_integer(b); + + r = json_build(&ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(x + y)))); + if (r < 0) + return r; + + return varlink_reply(link, ret); +} + +static int method_done(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + if (++n_done == 2) + sd_event_exit(varlink_get_event(link), EXIT_FAILURE); + + return 0; +} + +static int reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) { + JsonVariant *sum; + + sum = json_variant_by_key(parameters, "sum"); + + assert_se(json_variant_integer(sum) == 7+22); + + if (++n_done == 2) + sd_event_exit(varlink_get_event(link), EXIT_FAILURE); + + return 0; +} + +static int on_connect(VarlinkServer *s, Varlink *link, void *userdata) { + uid_t uid = UID_INVALID; + + assert(s); + assert(link); + + assert_se(varlink_get_peer_uid(link, &uid) >= 0); + assert_se(getuid() == uid); + + return 0; +} + +static int overload_reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) { + + /* This method call reply should always be called with a disconnection, since the method call should + * be talking to an overloaded server */ + + log_debug("Over reply triggered with error: %s", strna(error_id)); + assert_se(streq(error_id, VARLINK_ERROR_DISCONNECTED)); + sd_event_exit(varlink_get_event(link), 0); + + return 0; +} + +static void flood_test(const char *address) { + _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + _cleanup_free_ Varlink **connections = NULL; + size_t k; + char x = 'x'; + + log_debug("Flooding server..."); + + /* Block the main event loop while we flood */ + assert_se(write(block_write_fd, &x, sizeof(x)) == sizeof(x)); + + assert_se(sd_event_default(&e) >= 0); + + /* Flood the server with connections */ + assert_se(connections = new0(Varlink*, OVERLOAD_CONNECTIONS)); + for (k = 0; k < OVERLOAD_CONNECTIONS; k++) { + _cleanup_free_ char *t = NULL; + log_debug("connection %zu", k); + assert_se(varlink_connect_address(connections + k, address) >= 0); + + assert_se(asprintf(&t, "flood-%zu", k) >= 0); + assert_se(varlink_set_description(connections[k], t) >= 0); + assert_se(varlink_attach_event(connections[k], e, k) >= 0); + assert_se(varlink_sendb(connections[k], "io.test.Rubbish", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("id", JSON_BUILD_INTEGER(k)))) >= 0); + } + + /* Then, create one more, which should fail */ + log_debug("Creating overload connection..."); + assert_se(varlink_connect_address(&c, address) >= 0); + assert_se(varlink_set_description(c, "overload-client") >= 0); + assert_se(varlink_attach_event(c, e, k) >= 0); + assert_se(varlink_bind_reply(c, overload_reply) >= 0); + assert_se(varlink_invokeb(c, "io.test.Overload", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("foo", JSON_BUILD_STRING("bar")))) >= 0); + + /* Unblock it */ + log_debug("Unblocking server..."); + block_write_fd = safe_close(block_write_fd); + + /* This loop will terminate as soon as the overload reply callback is called */ + assert_se(sd_event_loop(e) >= 0); + + /* And close all connections again */ + for (k = 0; k < OVERLOAD_CONNECTIONS; k++) + connections[k] = varlink_unref(connections[k]); +} + +static void *thread(void *arg) { + _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *i = NULL; + JsonVariant *o = NULL; + const char *e; + + assert_se(json_build(&i, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(88)), + JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(99)))) >= 0); + + assert_se(varlink_connect_address(&c, arg) >= 0); + assert_se(varlink_set_description(c, "thread-client") >= 0); + + assert_se(varlink_call(c, "io.test.DoSomething", i, &o, &e, NULL) >= 0); + assert_se(json_variant_integer(json_variant_by_key(o, "sum")) == 88 + 99); + assert_se(!e); + + assert_se(varlink_callb(c, "io.test.IDontExist", &o, &e, NULL, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0); + assert_se(streq_ptr(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist")); + assert_se(streq(e, VARLINK_ERROR_METHOD_NOT_FOUND)); + + flood_test(arg); + + assert_se(varlink_send(c, "io.test.Done", NULL) >= 0); + + return NULL; +} + +static int block_fd_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + char c; + + assert_se(fd_nonblock(fd, false) >= 0); + + assert_se(read(fd, &c, sizeof(c)) == sizeof(c)); + /* When a character is written to this pipe we'll block until the pipe is closed. */ + + assert_se(read(fd, &c, sizeof(c)) == 0); + + assert_se(fd_nonblock(fd, true) >= 0); + + assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); + + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_source_unrefp) sd_event_source *block_event = NULL; + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + _cleanup_(close_pairp) int block_fds[2] = { -1, -1 }; + pthread_t t; + const char *sp; + + log_set_max_level(LOG_DEBUG); + log_open(); + + assert_se(mkdtemp_malloc("/tmp/varlink-test-XXXXXX", &tmpdir) >= 0); + sp = strjoina(tmpdir, "/socket"); + + assert_se(sd_event_default(&e) >= 0); + + assert_se(pipe2(block_fds, O_NONBLOCK|O_CLOEXEC) >= 0); + assert_se(sd_event_add_io(e, &block_event, block_fds[0], EPOLLIN, block_fd_handler, NULL) >= 0); + assert_se(sd_event_source_set_priority(block_event, SD_EVENT_PRIORITY_IMPORTANT) >= 0); + block_write_fd = TAKE_FD(block_fds[1]); + + assert_se(varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID) >= 0); + assert_se(varlink_server_set_description(s, "our-server") >= 0); + + assert_se(varlink_server_bind_method(s, "io.test.DoSomething", method_something) >= 0); + assert_se(varlink_server_bind_method(s, "io.test.Done", method_done) >= 0); + assert_se(varlink_server_bind_connect(s, on_connect) >= 0); + assert_se(varlink_server_listen_address(s, sp, 0600) >= 0); + assert_se(varlink_server_attach_event(s, e, 0) >= 0); + assert_se(varlink_server_set_connections_max(s, OVERLOAD_CONNECTIONS) >= 0); + + assert_se(varlink_connect_address(&c, sp) >= 0); + assert_se(varlink_set_description(c, "main-client") >= 0); + assert_se(varlink_bind_reply(c, reply) >= 0); + + assert_se(json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(7)), + JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(22)))) >= 0); + + assert_se(varlink_invoke(c, "io.test.DoSomething", v) >= 0); + + assert_se(varlink_attach_event(c, e, 0) >= 0); + + assert_se(pthread_create(&t, NULL, thread, (void*) sp) == 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(pthread_join(t, NULL) == 0); + + return 0; +} diff --git a/src/time-wait-sync/time-wait-sync.c b/src/time-wait-sync/time-wait-sync.c index d02633c9a8..f4d20af2d4 100644 --- a/src/time-wait-sync/time-wait-sync.c +++ b/src/time-wait-sync/time-wait-sync.c @@ -241,7 +241,7 @@ static int run(int argc, char * argv[]) { } if (state.has_watchfile) - log_debug("Exit enabled by: /run/systemd/timesync/synchonized"); + log_debug("Exit enabled by: /run/systemd/timesync/synchronized"); if (state.adjtime_state == TIME_ERROR) log_info("Exit without adjtimex synchronized."); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 406ca1df66..e03f1796e7 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -866,6 +866,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); + log_show_color(true); log_parse_environment(); log_open(); diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 4c00fa409f..3c3a7fe69a 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -1024,7 +1024,7 @@ static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t re sd_network_monitor_flush(m->network_monitor); /* When manager_network_read_link_servers() failed, we assume that the servers are changed. */ - changed = !!manager_network_read_link_servers(m); + changed = manager_network_read_link_servers(m); /* check if the machine is online */ online = network_is_online(); diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 73d40c0948..e56e09ca8c 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -52,7 +52,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { if (geteuid() == 0) { /* Try to fix the access mode, so that we can still - touch the file after dropping priviliges */ + touch the file after dropping privileges */ r = fchmod_and_chown(fd, 0644, uid, gid); if (r < 0) log_warning_errno(r, "Failed to chmod or chown %s, ignoring: %m", CLOCK_FILE); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index d9d1cc1c1a..73e0e33d0a 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -2397,7 +2397,7 @@ static int specifier_expansion_from_arg(Item *i) { assert(i); - if (i->argument == NULL) + if (!i->argument) return 0; switch (i->type) { diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c index 8ea2e1e327..9aca82b1fb 100644 --- a/src/udev/ata_id/ata_id.c +++ b/src/udev/ata_id/ata_id.c @@ -381,7 +381,7 @@ static int disk_identify(int fd, } out: - if (out_is_packet_device != NULL) + if (out_is_packet_device) *out_is_packet_device = is_packet_device; return ret; } @@ -432,7 +432,7 @@ int main(int argc, char *argv[]) { } node = argv[optind]; - if (node == NULL) { + if (!node) { log_error("no node specified"); return 1; } diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c index 3f882f557b..a287901266 100644 --- a/src/udev/cdrom_id/cdrom_id.c +++ b/src/udev/cdrom_id/cdrom_id.c @@ -95,7 +95,7 @@ static bool is_mounted(const char *device) { return false; fp = fopen("/proc/self/mountinfo", "re"); - if (fp == NULL) + if (!fp) return false; while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { if (makedev(maj, min) == statbuf.st_rdev) { @@ -108,11 +108,10 @@ static bool is_mounted(const char *device) { } static void info_scsi_cmd_err(const char *cmd, int err) { - if (err == -1) { + if (err == -1) log_debug("%s failed", cmd); - return; - } - log_debug("%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh", cmd, SK(err), ASC(err), ASCQ(err)); + else + log_debug("%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh", cmd, SK(err), ASC(err), ASCQ(err)); } struct scsi_cmd { @@ -149,9 +148,9 @@ static int scsi_cmd_run(struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t cmd->sg_io.dxferp = buf; cmd->sg_io.dxfer_len = bufsize; cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; - } else { + } else cmd->sg_io.dxfer_direction = SG_DXFER_NONE; - } + if (ioctl(fd, SG_IO, &cmd->sg_io)) return -1; @@ -191,7 +190,7 @@ static int media_eject(int fd) { scsi_cmd_set(&sc, 4, 0x02); scsi_cmd_set(&sc, 5, 0); err = scsi_cmd_run(&sc, fd, NULL, 0); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("START_STOP_UNIT", err); return -1; } @@ -202,10 +201,8 @@ static int cd_capability_compat(int fd) { int capability; capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); - if (capability < 0) { - log_debug("CDROM_GET_CAPABILITY failed"); - return -1; - } + if (capability < 0) + return log_debug_errno(errno, "CDROM_GET_CAPABILITY failed"); if (capability & CDC_CD_R) cd_cd_r = 1; @@ -225,10 +222,9 @@ static int cd_capability_compat(int fd) { } static int cd_media_compat(int fd) { - if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { - log_debug("CDROM_DRIVE_STATUS != CDS_DISC_OK"); - return -1; - } + if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) + return log_debug_errno(errno, "CDROM_DRIVE_STATUS != CDS_DISC_OK"); + cd_media = 1; return 0; } @@ -243,15 +239,13 @@ static int cd_inquiry(int fd) { scsi_cmd_set(&sc, 4, 36); scsi_cmd_set(&sc, 5, 0); err = scsi_cmd_run(&sc, fd, inq, 36); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("INQUIRY", err); return -1; } - if ((inq[0] & 0x1F) != 5) { - log_debug("not an MMC unit"); - return -1; - } + if ((inq[0] & 0x1F) != 5) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "not an MMC unit"); log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32); return 0; @@ -465,7 +459,7 @@ static int cd_profiles_old_mmc(int fd) { scsi_cmd_set(&sc, 8, sizeof(header)); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, header, sizeof(header)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ DISC INFORMATION", err); if (cd_media == 1) { log_debug("no current profile, but disc is present; assuming CD-ROM"); @@ -473,10 +467,9 @@ static int cd_profiles_old_mmc(int fd) { cd_media_track_count = 1; cd_media_track_count_data = 1; return 0; - } else { - log_debug("no current profile, assuming no media"); - return -1; - } + } else + return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM), + "no current profile, assuming no media"); }; cd_media = 1; @@ -512,7 +505,7 @@ static int cd_profiles(int fd) { scsi_cmd_set(&sc, 8, 8); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, features, 8); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("GET CONFIGURATION", err); /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ if (SK(err) == 0x5 && IN_SET(ASC(err), 0x20, 0x24)) { @@ -528,9 +521,8 @@ static int cd_profiles(int fd) { log_debug("current profile 0x%02x", cur_profile); feature_profile_media(cur_profile); ret = 0; /* we have media */ - } else { + } else log_debug("no current profile, assuming no media"); - } len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); @@ -548,7 +540,7 @@ static int cd_profiles(int fd) { scsi_cmd_set(&sc, 8, len & 0xff); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, features, len); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("GET CONFIGURATION", err); return -1; } @@ -598,7 +590,7 @@ static int cd_media_info(int fd) { scsi_cmd_set(&sc, 8, sizeof(header) & 0xff); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, header, sizeof(header)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ DISC INFORMATION", err); return -1; }; @@ -611,7 +603,7 @@ static int cd_media_info(int fd) { if (!cd_media_cd_rom) cd_media_state = media_status[header[2] & 3]; - /* fresh DVD-RW in restricted overwite mode reports itself as + /* fresh DVD-RW in restricted overwrite mode reports itself as * "appendable"; change it to "blank" to make it consistent with what * gets reported after blanking, and what userspace expects */ if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) @@ -637,7 +629,7 @@ static int cd_media_info(int fd) { scsi_cmd_set(&sc, 9, sizeof(dvdstruct)); scsi_cmd_set(&sc, 11, 0); err = scsi_cmd_run(&sc, fd, dvdstruct, sizeof(dvdstruct)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ DVD STRUCTURE", err); return -1; } @@ -653,16 +645,15 @@ static int cd_media_info(int fd) { scsi_cmd_set(&sc, 8, sizeof(format)); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, format, sizeof(format)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ DVD FORMAT CAPACITIES", err); return -1; } len = format[3]; - if (len & 7 || len < 16) { - log_debug("invalid format capacities length"); - return -1; - } + if (len & 7 || len < 16) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "invalid format capacities length"); switch(format[8] & 3) { case 1: @@ -680,8 +671,8 @@ static int cd_media_info(int fd) { case 3: cd_media = 0; //return no media - log_debug("format capacities returned no media"); - return -1; + return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM), + "format capacities returned no media"); } } @@ -695,7 +686,7 @@ static int cd_media_info(int fd) { scsi_cmd_set(&sc, 8, 32); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, buffer, sizeof(buffer)); - if ((err != 0)) { + if (err != 0) { cd_media = 0; info_scsi_cmd_err("READ FIRST 32 BLOCKS", err); return -1; @@ -748,7 +739,7 @@ static int cd_media_toc(int fd) { scsi_cmd_set(&sc, 8, sizeof(header) & 0xff); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, header, sizeof(header)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ TOC", err); return -1; } @@ -773,7 +764,7 @@ static int cd_media_toc(int fd) { scsi_cmd_set(&sc, 8, len & 0xff); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, toc, len); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ TOC (tracks)", err); return -1; } @@ -803,7 +794,7 @@ static int cd_media_toc(int fd) { scsi_cmd_set(&sc, 8, sizeof(header)); scsi_cmd_set(&sc, 9, 0); err = scsi_cmd_run(&sc, fd, header, sizeof(header)); - if ((err != 0)) { + if (err != 0) { info_scsi_cmd_err("READ TOC (multi session)", err); return -1; } @@ -1026,7 +1017,7 @@ work: if (cd_media_hddvd_rw) printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); - if (cd_media_state != NULL) + if (cd_media_state) printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); if (cd_media_session_next > 0) printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", cd_media_session_next); diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index a26c4cefa8..7754959cdf 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -159,6 +159,14 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { if (link->speed > UINT_MAX) return -ERANGE; + if (set_isempty(link->match_mac) && strv_isempty(link->match_path) && + strv_isempty(link->match_driver) && strv_isempty(link->match_type) && + strv_isempty(link->match_name) && !link->conditions) + log_warning("%s: No valid settings found in the [Match] section. " + "The file will match all interfaces. " + "If that is intended, please add OriginalName=* in the [Match] section.", + filename); + if (!condition_test_list(link->conditions, NULL, NULL, NULL)) { log_debug("%s: Conditions do not match the system environment, skipping.", filename); return 0; diff --git a/src/udev/net/naming-scheme.h b/src/udev/net/naming-scheme.h index 1f7cb0ccb9..4061eec99e 100644 --- a/src/udev/net/naming-scheme.h +++ b/src/udev/net/naming-scheme.h @@ -5,7 +5,7 @@ #include "macro.h" -/* So here's the deal: net_id is supposed to be an excercise in providing stable names for network devices. However, we +/* So here's the deal: net_id is supposed to be an exercise in providing stable names for network devices. However, we * also want to keep updating the naming scheme used in future versions of net_id. These two goals of course are * contradictory: on one hand we want things to not change and on the other hand we want them to improve. Our way out * of this dilemma is to introduce the "naming scheme" concept: each time we improve the naming logic we define a new @@ -25,15 +25,16 @@ typedef enum NamingSchemeFlags { NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */ NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df887797c9e05074a562ddacdcdf5e */ - NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Allow zero acpi_index field, see d81186ef4f6a888a70f20a1e73a812d6acb9e22f */ + NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Use zero acpi_index field, see d81186ef4f6a888a70f20a1e73a812d6acb9e22f */ NAMING_ALLOW_RERENAMES = 1 << 4, /* Allow re-renaming of devices, see #9006 */ - NAMING_NETDEVSIM = 1 << 5, /* Allow re-renaming of netdevsim devices */ + NAMING_NETDEVSIM = 1 << 5, /* Generate names for netdevsim devices, see eaa9d507d85509c8bf727356e3884ec54b0fc646 */ + NAMING_LABEL_NOPREFIX = 1 << 6, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */ /* And now the masks that combine the features above */ NAMING_V238 = 0, NAMING_V239 = NAMING_V238 | NAMING_SR_IOV_V | NAMING_NPAR_ARI, NAMING_V240 = NAMING_V239 | NAMING_INFINIBAND | NAMING_ZERO_ACPI_INDEX | NAMING_ALLOW_RERENAMES, - NAMING_V243 = NAMING_V240 | NAMING_NETDEVSIM, + NAMING_V243 = NAMING_V240 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX, _NAMING_SCHEME_FLAGS_INVALID = -1, } NamingSchemeFlags; diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 2698cdd82f..e1a8d7e3a9 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -157,7 +157,7 @@ static int get_file_options(const char *vendor, const char *model, int retval = 0; f = fopen(config_file, "re"); - if (f == NULL) { + if (!f) { if (errno == ENOENT) return 1; else { @@ -181,7 +181,7 @@ static int get_file_options(const char *vendor, const char *model, vendor_in = model_in = options_in = NULL; buf = fgets(buffer, MAX_BUFFER_LEN, f); - if (buf == NULL) + if (!buf) break; lineno++; if (buf[strlen(buffer) - 1] != '\n') { @@ -239,7 +239,7 @@ static int get_file_options(const char *vendor, const char *model, break; } if (vendor == NULL) { - if (vendor_in == NULL) + if (!vendor_in) break; } else if (vendor_in && startswith(vendor, vendor_in) && @@ -346,18 +346,18 @@ static int set_options(int argc, char **argv, default_page_code = PAGE_83; else if (streq(optarg, "pre-spc3-83")) default_page_code = PAGE_83_PRE_SPC3; - else { - log_error("Unknown page code '%s'", optarg); - return -1; - } + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown page code '%s'", + optarg); break; case 's': sg_version = atoi(optarg); - if (sg_version < 3 || sg_version > 4) { - log_error("Unknown SG version '%s'", optarg); - return -1; - } + if (sg_version < 3 || sg_version > 4) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown SG version '%s'", + optarg); break; case 'u': diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index 7ca01858d1..e1940e7d38 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -168,10 +168,10 @@ static int scsi_dump_sense(struct scsi_id_device *dev_scsi, * we'll retry the command. */ - if (sb_len < 1) { - log_debug("%s: sense buffer empty", dev_scsi->kernel); - return -1; - } + if (sb_len < 1) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: sense buffer empty", + dev_scsi->kernel); sense_class = (sense_buffer[0] >> 4) & 0x07; code = sense_buffer[0] & 0xf; @@ -181,40 +181,41 @@ static int scsi_dump_sense(struct scsi_id_device *dev_scsi, * extended sense data. */ s = sense_buffer[7] + 8; - if (sb_len < s) { - log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, s - sb_len); - return -1; - } + if (sb_len < s) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: sense buffer too small %d bytes, %d bytes too short", + dev_scsi->kernel, sb_len, + s - sb_len); + if (IN_SET(code, 0x0, 0x1)) { sense_key = sense_buffer[2] & 0xf; - if (s < 14) { + if (s < 14) /* * Possible? */ - log_debug("%s: sense result too" " small %d bytes", - dev_scsi->kernel, s); - return -1; - } + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: sense result too small %d bytes", + dev_scsi->kernel, s); + asc = sense_buffer[12]; ascq = sense_buffer[13]; } else if (IN_SET(code, 0x2, 0x3)) { sense_key = sense_buffer[1] & 0xf; asc = sense_buffer[2]; ascq = sense_buffer[3]; - } else { - log_debug("%s: invalid sense code 0x%x", - dev_scsi->kernel, code); - return -1; - } + } else + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: invalid sense code 0x%x", + dev_scsi->kernel, code); + log_debug("%s: sense key 0x%x ASC 0x%x ASCQ 0x%x", dev_scsi->kernel, sense_key, asc, ascq); } else { - if (sb_len < 4) { - log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, 4 - sb_len); - return -1; - } + if (sb_len < 4) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: sense buffer too small %d bytes, %d bytes too short", + dev_scsi->kernel, sb_len, + 4 - sb_len); if (sense_buffer[0] < 15) log_debug("%s: old sense key: 0x%x", dev_scsi->kernel, sense_buffer[0] & 0x0f); @@ -231,13 +232,13 @@ static int scsi_dump_sense(struct scsi_id_device *dev_scsi, static int scsi_dump(struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) { if (!io->status && !io->host_status && !io->msg_status && - !io->driver_status) { + !io->driver_status) /* * Impossible, should not be called. */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: called with no error", + __FUNCTION__); log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x", dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); @@ -249,13 +250,13 @@ static int scsi_dump(struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) { static int scsi_dump_v4(struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) { if (!io->device_status && !io->transport_status && - !io->driver_status) { + !io->driver_status) /* * Impossible, should not be called. */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: called with no error", + __FUNCTION__); log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x", dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status); @@ -278,10 +279,9 @@ static int scsi_inquiry(struct scsi_id_device *dev_scsi, int fd, int retry = 3; /* rather random */ int retval; - if (buflen > SCSI_INQ_BUFF_LEN) { - log_debug("buflen %d too long", buflen); - return -1; - } + if (buflen > SCSI_INQ_BUFF_LEN) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "buflen %d too long", buflen); resend: if (dev_scsi->use_sg == 4) { @@ -388,7 +388,7 @@ static int do_scsi_page0_inquiry(struct scsi_id_device *dev_scsi, int fd, * If the vendor id appears in the page assume the page is * invalid. */ - if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { + if (strneq((char*) buffer + VENDOR_LENGTH, dev_scsi->vendor, VENDOR_LENGTH)) { log_debug("%s: invalid page0 data", dev_scsi->kernel); return 1; } @@ -396,27 +396,21 @@ static int do_scsi_page0_inquiry(struct scsi_id_device *dev_scsi, int fd, return 0; } -/* - * The caller checks that serial is long enough to include the vendor + - * model. - */ -static int prepend_vendor_model(struct scsi_id_device *dev_scsi, char *serial) { - int ind; - - strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); - strncat(serial, dev_scsi->model, MODEL_LENGTH); - ind = strlen(serial); - - /* - * This is not a complete check, since we are using strncat/cpy - * above, ind will never be too large. - */ - if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { - log_debug("%s: expected length %d, got length %d", - dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); - return -1; - } - return ind; +static int append_vendor_model( + const struct scsi_id_device *dev_scsi, + char buf[static VENDOR_LENGTH + MODEL_LENGTH]) { + + if (strnlen(dev_scsi->vendor, VENDOR_LENGTH) != VENDOR_LENGTH) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: bad vendor string \"%s\"", + dev_scsi->kernel, dev_scsi->vendor); + if (strnlen(dev_scsi->model, MODEL_LENGTH) != MODEL_LENGTH) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: bad model string \"%s\"", + dev_scsi->kernel, dev_scsi->model); + memcpy(buf, dev_scsi->vendor, VENDOR_LENGTH); + memcpy(buf + VENDOR_LENGTH, dev_scsi->model, MODEL_LENGTH); + return VENDOR_LENGTH + MODEL_LENGTH; } /* @@ -497,7 +491,7 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, * included in the identifier. */ if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - if (prepend_vendor_model(dev_scsi, &serial[1]) < 0) + if (append_vendor_model(dev_scsi, serial + 1) < 0) return 1; i = 4; /* offset to the start of the identifier */ @@ -520,12 +514,12 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, } } - strcpy(serial_short, &serial[s]); + strcpy(serial_short, serial + s); if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { - strncpy(wwn, &serial[s], 16); - if (wwn_vendor_extension != NULL) - strncpy(wwn_vendor_extension, &serial[s + 16], 16); + strncpy(wwn, serial + s, 16); + if (wwn_vendor_extension) + strncpy(wwn_vendor_extension, serial + s + 16, 16); } return 0; @@ -619,8 +613,8 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, * one or a small number of descriptors. */ for (j = 4; j <= (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) { - retval = check_fill_0x83_id(dev_scsi, &page_83[j], - &id_search_list[id_ind], + retval = check_fill_0x83_id(dev_scsi, page_83 + j, + id_search_list + id_ind, serial, serial_short, len, wwn, wwn_vendor_extension, tgpt_group); @@ -729,17 +723,17 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, * specific type where we prepend '0' + vendor + model. */ len = buf[3]; - if (serial != NULL) { + if (serial) { serial[0] = 'S'; - ser_ind = prepend_vendor_model(dev_scsi, &serial[1]); + ser_ind = append_vendor_model(dev_scsi, serial + 1); if (ser_ind < 0) return 1; ser_ind++; /* for the leading 'S' */ for (i = 4; i < len + 4; i++, ser_ind++) serial[ser_ind] = buf[i]; } - if (serial_short != NULL) { - memcpy(serial_short, &buf[4], len); + if (serial_short) { + memcpy(serial_short, buf + 4, len); serial_short[len] = '\0'; } return 0; diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 2c7dcf7d2a..c487bc00df 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -9,86 +9,7 @@ * * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames * - * Two character prefixes based on the type of interface: - * en — Ethernet - * ib — InfiniBand - * sl — serial line IP (slip) - * wl — wlan - * ww — wwan - * - * Type of names: - * b<number> — BCMA bus core number - * c<bus_id> — bus id of a grouped CCW or CCW device, - * with all leading zeros stripped [s390] - * o<index>[n<phys_port_name>|d<dev_port>] - * — on-board device index number - * s<slot>[f<function>][n<phys_port_name>|d<dev_port>] - * — hotplug slot index number - * x<MAC> — MAC address - * [P<domain>]p<bus>s<slot>[f<function>][n<phys_port_name>|d<dev_port>] - * — PCI geographical location - * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] - * — USB port number chain - * v<slot> - VIO slot number (IBM PowerVM) - * a<vendor><model>i<instance> — Platform bus ACPI instance id - * i<addr>n<phys_port_name> — Netdevsim bus address and port name - * - * All multi-function PCI devices will carry the [f<function>] number in the - * device name, including the function 0 device. - * - * SR-IOV virtual devices are named based on the name of the parent interface, - * with a suffix of "v<N>", where <N> is the virtual device number. - * - * When using PCI geography, The PCI domain is only prepended when it is not 0. - * - * For USB devices the full chain of port numbers of hubs is composed. If the - * name gets longer than the maximum number of 15 characters, the name is not - * exported. - * The usual USB configuration == 1 and interface == 0 values are suppressed. - * - * PCI Ethernet card with firmware index "1": - * ID_NET_NAME_ONBOARD=eno1 - * ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1 - * - * PCI Ethernet card in hotplug slot with firmware index number: - * /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1 - * ID_NET_NAME_MAC=enx000000000466 - * ID_NET_NAME_PATH=enp5s0 - * ID_NET_NAME_SLOT=ens1 - * - * PCI Ethernet multi-function card with 2 ports: - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 - * ID_NET_NAME_MAC=enx78e7d1ea46da - * ID_NET_NAME_PATH=enp2s0f0 - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 - * ID_NET_NAME_MAC=enx78e7d1ea46dc - * ID_NET_NAME_PATH=enp2s0f1 - * - * PCI wlan card: - * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 - * ID_NET_NAME_MAC=wlx0024d7e31130 - * ID_NET_NAME_PATH=wlp3s0 - * - * PCI IB host adapter with 2 ports: - * /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.0/net/ibp21s0f0 - * ID_NET_NAME_PATH=ibp21s0f0 - * /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.1/net/ibp21s0f1 - * ID_NET_NAME_PATH=ibp21s0f1 - * - * USB built-in 3G modem: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 - * ID_NET_NAME_MAC=wwx028037ec0200 - * ID_NET_NAME_PATH=wwp0s29u1u4i6 - * - * USB Android phone: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 - * ID_NET_NAME_MAC=enxd626b3450fb5 - * ID_NET_NAME_PATH=enp0s29u1u2 - * - * s390 grouped CCW interface: - * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 - * ID_NET_NAME_MAC=enx026d3c00000a - * ID_NET_NAME_PATH=encf5f0 + * When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too. */ #include <errno.h> @@ -249,7 +170,7 @@ static int dev_pci_onboard(sd_device *dev, struct netnames *names) { char *s; int r; - /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ + /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ if (sd_device_get_sysattr_value(names->pcidev, "acpi_index", &attr) < 0) { /* SMBIOS type 41 — Onboard Devices Extended Information */ r = sd_device_get_sysattr_value(names->pcidev, "index", &attr); @@ -263,10 +184,11 @@ static int dev_pci_onboard(sd_device *dev, struct netnames *names) { if (idx == 0 && !naming_scheme_has(NAMING_ZERO_ACPI_INDEX)) return -EINVAL; - /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for - * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary - * cut-off, which is somewhere beyond the realistic number of physical network interface a system might - * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */ + /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to + * report for example). Let's define a cut-off where we don't consider the index reliable anymore. We + * pick some arbitrary cut-off, which is somewhere beyond the realistic number of physical network + * interface a system might have. Ideally the kernel would already filter his crap for us, but it + * doesn't currently. */ if (idx > ONBOARD_INDEX_MAX) return -ENOENT; @@ -722,9 +644,8 @@ static int names_ccw(sd_device *dev, struct netnames *names) { if (r < 0) return r; - /* Check the length of the bus-ID. Rely on that the kernel provides - * a correct bus-ID; alternatively, improve this check and parse and - * verify each bus-ID part... + /* Check the length of the bus-ID. Rely on the fact that the kernel provides a correct bus-ID; + * alternatively, improve this check and parse and verify each bus-ID part... */ bus_id_len = strlen(bus_id); if (!IN_SET(bus_id_len, 8, 9)) @@ -960,7 +881,9 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) { udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str); if (names.pci_onboard_label && - snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_onboard_label)) + snprintf_ok(str, sizeof str, "%s%s", + naming_scheme_has(NAMING_LABEL_NOPREFIX) ? "" : prefix, + names.pci_onboard_label)) udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str); if (names.pci_path[0] && diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 7bdf6cfbb5..58af63f11a 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -196,7 +196,7 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) { desc->bInterfaceProtocol) != 7) continue; - if (strstr(ifs_str, if_str) != NULL) + if (strstr(ifs_str, if_str)) continue; memcpy(&ifs_str[strpos], if_str, 8), diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 140ec35293..a6f7ee82bb 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -549,6 +549,7 @@ static int worker_spawn(Manager *manager, struct event *event) { } static void event_run(Manager *manager, struct event *event) { + static bool log_children_max_reached = true; struct worker *worker; Iterator i; int r; @@ -573,11 +574,19 @@ static void event_run(Manager *manager, struct event *event) { } if (hashmap_size(manager->workers) >= arg_children_max) { - if (arg_children_max > 1) + + /* Avoid spamming the debug logs if the limit is already reached and + * many events still need to be processed */ + if (log_children_max_reached && arg_children_max > 1) { log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers)); + log_children_max_reached = false; + } return; } + /* Re-enable the debug message for the next batch of events */ + log_children_max_reached = true; + /* start new worker and pass initial device */ worker_spawn(manager, event); } diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c index 4d60ee3c85..5de26b8062 100644 --- a/src/udev/v4l_id/v4l_id.c +++ b/src/udev/v4l_id/v4l_id.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { } device = argv[optind]; - if (device == NULL) + if (!device) return 2; fd = open(device, O_RDONLY); diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 140e0badd2..d2bc3921d2 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -237,7 +237,7 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { /* * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512 * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always - * requries 32 per glyph, regardless of the actual height - see the comment above #define + * requires 32 per glyph, regardless of the actual height - see the comment above #define * max_font_size 65536 in drivers/tty/vt/vt.c for more details. */ fontbuf = malloc_multiply((cfo.width + 7) / 8 * 32, cfo.charcount); diff --git a/test/TEST-20-MAINPIDGAMES/testsuite.sh b/test/TEST-20-MAINPIDGAMES/testsuite.sh index 9471a5d734..904ac0e3b8 100755 --- a/test/TEST-20-MAINPIDGAMES/testsuite.sh +++ b/test/TEST-20-MAINPIDGAMES/testsuite.sh @@ -42,11 +42,11 @@ test `systemctl show -p MainPID --value testsuite.service` -eq $$ systemd-notify MAINPID=1073741824 test `systemctl show -p MainPID --value testsuite.service` -eq $$ -# Change it again to the external PID, without priviliges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges. +# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges. systemd-notify --uid=1000 MAINPID=$EXTERNALPID test `systemctl show -p MainPID --value testsuite.service` -eq $$ -# Change it again to the internal PID, without priviliges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges. +# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges. systemd-notify --uid=1000 MAINPID=$INTERNALPID test `systemctl show -p MainPID --value testsuite.service` -eq $INTERNALPID diff --git a/test/TEST-21-SYSUSERS/unhappy-2.input b/test/TEST-21-SYSUSERS/unhappy-2.input index 5be0e6d187..3266b2229b 100644 --- a/test/TEST-21-SYSUSERS/unhappy-2.input +++ b/test/TEST-21-SYSUSERS/unhappy-2.input @@ -1,4 +1,4 @@ -# Ensure it is not allowed to create groups implicitely in the uid:gid syntax +# Ensure it is not allowed to create groups implicitly in the uid:gid syntax # #Type Name ID GECOS HOMEDIR u u1 100:100 - diff --git a/test/TEST-22-TMPFILES/test-03.sh b/test/TEST-22-TMPFILES/test-03.sh index 39a4badc38..8d009fb5bb 100755 --- a/test/TEST-22-TMPFILES/test-03.sh +++ b/test/TEST-22-TMPFILES/test-03.sh @@ -212,7 +212,7 @@ EOF test -f /tmp/w/overwritten test "$(< /tmp/w/overwritten)" = "new content" -### writing into an 'exotic' file sould be allowed. +### writing into an 'exotic' file should be allowed. systemd-tmpfiles --create - <<EOF w /dev/null - - - - new content EOF diff --git a/test/dml-passthrough-set-ml.service b/test/dml-passthrough-set-ml.service index 2abd591389..2e568b5deb 100644 --- a/test/dml-passthrough-set-ml.service +++ b/test/dml-passthrough-set-ml.service @@ -5,4 +5,4 @@ Description=DML passthrough set ML service Slice=dml-passthrough.slice Type=oneshot ExecStart=/bin/true -MemoryLow=25 +MemoryLow=0 diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index 128f8b6341..2f152cbd6d 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -11,6 +11,7 @@ ListenPort= PrivateKey= PrivateKeyFile= FwMark= +FirewallMark= [MACVTAP] Mode= [Match] @@ -31,6 +32,7 @@ FlowLabel= UDP6ZeroChecksumRx= Remote= UDP6ZeroCheckSumRx= +IPDoNotFragment= [Bridge] ForwardDelaySec= HelloTimeSec= @@ -92,6 +94,7 @@ Group= FDBAgeingSec= MacLearning= Id= +VNI= RemoteChecksumRx= TOS= L2MissNotification= @@ -99,6 +102,7 @@ UDP6ZeroChecksumTx= UDP6ZeroCheckSumRx= UDPCheckSum= GroupPolicyExtension= +GenericProtocolExtension= MaximumFDBEntries= TTL= DestinationPort= @@ -106,6 +110,7 @@ ReduceARPProxy= PortRange= UDPChecksum= UDP6ZeroCheckSumTx= +IPDoNotFragment= [VXCAN] Peer= [Bond] @@ -136,6 +141,8 @@ DynamicTransmitLoadBalancing= Protocol= Port= Encapsulation= +Local= +Peer= [Tap] MultiQueue= OneQueue= @@ -146,6 +153,9 @@ VNetHeader= [IPVLAN] Mode= Flags= +[IPVTAP] +Mode= +Flags= [Tun] OneQueue= MultiQueue= @@ -174,7 +184,7 @@ SessionId= PeerSessionId= Layer2SpecificHeader= Name= -[MACSEC] +[MACsec] Port= Encrypt= [MACsecReceiveAssociation] diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index cd2031150f..5861c41e75 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -10,6 +10,9 @@ MulticastToUnicast= MulticastFlood= NeighborSuppression= Learning= +ProxyARP= +ProxyARPWiFi= +MulticastRouter= [Match] KernelVersion= Type= @@ -32,6 +35,9 @@ MACAddress= [BridgeFDB] VLANId= MACAddress= +Destination= +VNI= +AssociatedWith= [DHCP] UseDomains= UseRoutes= @@ -57,6 +63,8 @@ ClientIdentifier= ListenPort= UseTimezone= RouteTable= +BlackList= +SendRelease= [Route] Destination= Protocol= @@ -72,6 +80,7 @@ PreferredSource= Scope= MTUBytes= QuickAck= +FastOpenNoCookie= Source= Metric= [Network] @@ -80,6 +89,7 @@ IPMasquerade= ProxyARP= PrimarySlave= IPv4LLRoute= +DefaultRouteOnDevice= Address= IPv6ProxyNDPAddress= IPv6AcceptRA= @@ -94,6 +104,7 @@ Tunnel= Gateway= IPv4LL= IPVLAN= +IPVTAP= EmitLLDP= IPv6MTUBytes= IPv4ProxyARP= @@ -176,7 +187,7 @@ Managed= OtherInformation= [Neighbor] Address= -MacAddress= +MACAddress= [IPv6AddressLabel] Label= Prefix= @@ -198,6 +209,7 @@ MaxLeaseTimeSec= DefaultLeaseTimeSec= EmitTimezone= DNS= +MaxAttempts= [DHCPv4] UseHostname= UseMTU= diff --git a/test/fuzz/fuzz-varlink/array b/test/fuzz/fuzz-varlink/array Binary files differnew file mode 100644 index 0000000000..f3ee40bcca --- /dev/null +++ b/test/fuzz/fuzz-varlink/array diff --git a/test/fuzz/fuzz-varlink/do-something b/test/fuzz/fuzz-varlink/do-something Binary files differnew file mode 100644 index 0000000000..3b124cb7d4 --- /dev/null +++ b/test/fuzz/fuzz-varlink/do-something diff --git a/test/fuzz/fuzz-varlink/huge-method b/test/fuzz/fuzz-varlink/huge-method new file mode 100644 index 0000000000..a480e417fc --- /dev/null +++ b/test/fuzz/fuzz-varlink/huge-method @@ -0,0 +1 @@ +{"method":" "} diff --git a/test/fuzz/fuzz-varlink/method-call b/test/fuzz/fuzz-varlink/method-call Binary files differnew file mode 100644 index 0000000000..8654a7c06d --- /dev/null +++ b/test/fuzz/fuzz-varlink/method-call diff --git a/test/fuzz/fuzz-varlink/method-error b/test/fuzz/fuzz-varlink/method-error Binary files differnew file mode 100644 index 0000000000..9ce68d8e4b --- /dev/null +++ b/test/fuzz/fuzz-varlink/method-error diff --git a/test/fuzz/fuzz-varlink/method-reply b/test/fuzz/fuzz-varlink/method-reply Binary files differnew file mode 100644 index 0000000000..cd4bd947ab --- /dev/null +++ b/test/fuzz/fuzz-varlink/method-reply diff --git a/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf b/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf Binary files differnew file mode 100644 index 0000000000..b0d8618298 --- /dev/null +++ b/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf diff --git a/test/mkosi.default.networkd-test b/test/mkosi.default.networkd-test index 9630d3ec42..58af352f6f 100644 --- a/test/mkosi.default.networkd-test +++ b/test/mkosi.default.networkd-test @@ -1,7 +1,7 @@ # Puts together an nspawn container and runs networkd-test.py in it, inside a # network namespace and everything. Run this with "mkosi # --default=mkosi.default.networkd-test boot". This will start the test and -# eventually exit with sucess in case the test succeeded. +# eventually exit with success in case the test succeeded. [Distribution] Distribution=fedora diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py index abb3116c97..3d9c1ddb87 100755 --- a/test/sysv-generator-test.py +++ b/test/sysv-generator-test.py @@ -48,7 +48,7 @@ class SysvGeneratorTest(unittest.TestCase): '''Run sysv-generator. Fail if stderr contains any "Fail", unless expect_error is True. - Return (stderr, filename -> ConfigParser) pair with ouput to stderr and + Return (stderr, filename -> ConfigParser) pair with output to stderr and parsed generated units. ''' env = os.environ.copy() diff --git a/test/test-functions b/test/test-functions index 50c6e96902..30b559097b 100644 --- a/test/test-functions +++ b/test/test-functions @@ -50,7 +50,7 @@ IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no) if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then STRIP_BINARIES=no - SKIP_INITRD=yes + SKIP_INITRD="${SKIP_INITRD:-yes}" PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan QEMU_MEM="1536M" QEMU_SMP=4 @@ -378,7 +378,12 @@ find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\ # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886. JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d mkdir -p "\$JOURNALD_CONF_DIR" -printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log\n" >"\$JOURNALD_CONF_DIR/env.conf" +printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS:log_path=/systemd-journald.ubsan.log\n" >"\$JOURNALD_CONF_DIR/env.conf" + +# Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path +# Let's try to catch them by redirecting stderr (and stdout just in case) to a file +# See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821 +printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf" # 90s isn't enough for some services to finish when literally everything is run # under ASan+UBSan in containers, which, in turn, are run in VMs. @@ -386,6 +391,22 @@ printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/sys mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf +# Let's override another hard-coded timeout that kicks in too early +mkdir -p /etc/systemd/system/systemd-journal-flush.service.d +printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf + +# The 'mount' utility doesn't behave well under libasan, causing unexpected +# fails during boot and subsequent test results check: +# bash-5.0# mount -o remount,rw -v / +# mount: /dev/sda1 mounted on /. +# bash-5.0# echo \$? +# 1 +# Let's workaround this by clearing the previously set LD_PRELOAD env variable, +# so the libasan library is not loaded for this particular service +REMOUNTFS_CONF_DIR=/etc/systemd/system/systemd-remount-fs.service.d +mkdir -p "\$REMOUNTFS_CONF_DIR" +printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$REMOUNTFS_CONF_DIR/env.conf" + export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS exec $ROOTLIBDIR/systemd "\$@" EOF @@ -496,9 +517,10 @@ check_asan_reports() { ret=$(($ret+1)) fi - journald_report=$(find "$root" -name "systemd-journald.asan.log*" -exec cat {} \;) + journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;) if [[ ! -z "$journald_report" ]]; then - printf "%s" "$journald_report" + printf "%s\n" "$journald_report" + cat "$root/systemd-journald.out" || true ret=$(($ret+1)) fi diff --git a/test/test-network/conf/25-ipvtap.netdev b/test/test-network/conf/25-ipvtap.netdev new file mode 100644 index 0000000000..cd6aec21ab --- /dev/null +++ b/test/test-network/conf/25-ipvtap.netdev @@ -0,0 +1,6 @@ +[NetDev] +Name=ipvtap99 +Kind=ipvtap + +[IPVLAN] +Mode=L2 diff --git a/test/test-network/conf/25-route-static.network b/test/test-network/conf/25-route-static.network index 50b2ce0c3b..9e12480a65 100644 --- a/test/test-network/conf/25-route-static.network +++ b/test/test-network/conf/25-route-static.network @@ -5,6 +5,8 @@ Name=dummy98 IPv6AcceptRA=no Address=2001:1234:5:8f63::1/128 Address=149.10.124.58/28 +DefaultRouteOnDevice=yes +IPv4LLRoute=yes [Route] Destination=2001:1234:5:8fff:ff:ff:ff:ff/128 diff --git a/test/test-network/conf/25-tunnel-local-any.network b/test/test-network/conf/25-tunnel-local-any.network new file mode 100644 index 0000000000..8ce05adbbb --- /dev/null +++ b/test/test-network/conf/25-tunnel-local-any.network @@ -0,0 +1,8 @@ +[Match] +Name=*tun98 *tap98 ip6tnl98 erspan98 + +[Network] +IPv6AcceptRA=no +Address=2001:db8:0:f102::17/64 +Address=10.3.2.4/16 +LinkLocalAddressing=yes diff --git a/test/test-network/conf/25-tunnel-remote-any.network b/test/test-network/conf/25-tunnel-remote-any.network new file mode 100644 index 0000000000..becdcaa8df --- /dev/null +++ b/test/test-network/conf/25-tunnel-remote-any.network @@ -0,0 +1,8 @@ +[Match] +Name=*tun97 ip6tnl97 + +[Network] +IPv6AcceptRA=no +Address=2001:db8:0:f102::18/64 +Address=10.3.2.5/16 +LinkLocalAddressing=yes diff --git a/test/test-network/conf/25-tunnel.network b/test/test-network/conf/25-tunnel.network new file mode 100644 index 0000000000..32c19867ad --- /dev/null +++ b/test/test-network/conf/25-tunnel.network @@ -0,0 +1,8 @@ +[Match] +Name=*tun99 *tap99 ip6tnl99 erspan99 + +[Network] +IPv6AcceptRA=no +Address=2001:db8:0:f102::16/64 +Address=10.3.2.3/16 +LinkLocalAddressing=yes diff --git a/test/test-network/conf/25-vxlan.netdev b/test/test-network/conf/25-vxlan.netdev index 819a58356f..d5acf48da5 100644 --- a/test/test-network/conf/25-vxlan.netdev +++ b/test/test-network/conf/25-vxlan.netdev @@ -3,7 +3,7 @@ Name=vxlan99 Kind=vxlan [VXLAN] -Id=999 +VNI=999 L2MissNotification=true L3MissNotification=true RouteShortCircuit=true diff --git a/test/test-network/conf/dhcp-client-ipv6-only.network b/test/test-network/conf/dhcp-client-ipv6-only.network index 1f70c3b86d..8b2e934f7f 100644 --- a/test/test-network/conf/dhcp-client-ipv6-only.network +++ b/test/test-network/conf/dhcp-client-ipv6-only.network @@ -3,3 +3,4 @@ Name=veth99 [Network] DHCP=ipv6 +IPv6Token=::1a:2b:3c:4d diff --git a/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network b/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network new file mode 100644 index 0000000000..9ebdbb4f8d --- /dev/null +++ b/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network @@ -0,0 +1,7 @@ +[Match] +Name=veth99 + +[Network] +DHCP=ipv4 +LinkLocalAddressing=fallback +IPv6AcceptRA=no diff --git a/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network b/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network new file mode 100644 index 0000000000..7faff5f9e5 --- /dev/null +++ b/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network @@ -0,0 +1,10 @@ +[Match] +Name=veth99 + +[Network] +DHCP=ipv4 +LinkLocalAddressing=fallback +IPv6AcceptRA=no + +[DHCP] +MaxAttempts=1 diff --git a/test/test-network/conf/ipvtap.network b/test/test-network/conf/ipvtap.network new file mode 100644 index 0000000000..c81ba52e2d --- /dev/null +++ b/test/test-network/conf/ipvtap.network @@ -0,0 +1,5 @@ +[Match] +Name=test1 + +[Network] +IPVTAP=ipvtap99 diff --git a/test/test-network/conf/vxlan-test1.network b/test/test-network/conf/vxlan-test1.network new file mode 100644 index 0000000000..32880873c2 --- /dev/null +++ b/test/test-network/conf/vxlan-test1.network @@ -0,0 +1,6 @@ +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=false +VXLAN=vxlan99 diff --git a/test/test-network/conf/vxlan.network b/test/test-network/conf/vxlan.network index 80b405574c..1b63785d15 100644 --- a/test/test-network/conf/vxlan.network +++ b/test/test-network/conf/vxlan.network @@ -1,5 +1,17 @@ [Match] -Name=test1 +Name=vxlan99 [Network] -VXLAN=vxlan99 +IPv6AcceptRA=no + +[BridgeFDB] +MACAddress=00:11:22:33:44:55 +Destination=10.0.0.5 + +[BridgeFDB] +MACAddress=00:11:22:33:44:66 +Destination=10.0.0.6 + +[BridgeFDB] +MACAddress=00:11:22:33:44:77 +Destination=10.0.0.7 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index d9db9c1657..5c8b5db9ea 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -70,6 +70,35 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable(): return f +def expectedFailureIf_ip6gre_do_not_support_ipv6ll(): + def f(func): + rc = subprocess.call(['ip', 'link', 'add', 'name', 'test1', 'type', 'dummy']) + if rc != 0: + return unittest.expectedFailure(func) + + time.sleep(1) + rc = subprocess.call(['ip', 'tunnel', 'add', 'tun99', 'local', '2a00:ffde:4567:edde::4986', 'remote', '2001:473:fece:cafe::5178', 'mode', 'ip6gre', 'dev', 'dummy99']) + if rc != 0: + return unittest.expectedFailure(func) + + time.sleep(1) + # Not sure why, but '0' or '2' do not work. + rc = subprocess.call(['sysctl', '-w', 'net.ipv6.conf.tun99.addr_gen_mode=3']) + if rc != 0: + return unittest.expectedFailure(func) + + time.sleep(1) + rc = subprocess.run(['ip', '-6', 'address', 'show', 'dev', 'tun99', 'scope', 'link'], stdout=subprocess.PIPE) + if rc.returncode != 0: + return unittest.expectedFailure(func) + + if 'inet6' not in rc.stdout.rstrip().decode('utf-8'): + return unittest.expectedFailure(func) + + return func + + return f + def setUpModule(): os.makedirs(network_unit_file_path, exist_ok=True) os.makedirs(networkd_ci_path, exist_ok=True) @@ -139,8 +168,8 @@ class Utilities(): if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))): shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d')) - def start_dnsmasq(self, additional_options=''): - dnsmasq_command = 'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range=2600::10,2600::20 --dhcp-range=192.168.5.10,192.168.5.200 -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options + def start_dnsmasq(self, additional_options='', lease_time='1h'): + dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range=2600::10,2600::20,{lease_time} --dhcp-range=192.168.5.10,192.168.5.200,{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options subprocess.check_call(dnsmasq_command, shell=True) time.sleep(10) @@ -222,6 +251,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'ipiptun98', 'ipiptun99', 'ipvlan99', + 'ipvtap99', 'isataptun99', 'macvlan99', 'macvtap99', @@ -289,6 +319,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ipip-tunnel-remote-any.netdev', '25-ipip-tunnel.netdev', '25-ipvlan.netdev', + '25-ipvtap.netdev', '25-isatap-tunnel.netdev', '25-macsec.key', '25-macsec.netdev', @@ -298,6 +329,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-sit-tunnel.netdev', '25-tap.netdev', '25-tun.netdev', + '25-tunnel-local-any.network', + '25-tunnel-remote-any.network', + '25-tunnel.network', '25-vcan.netdev', '25-veth.netdev', '25-vrf.netdev', @@ -324,6 +358,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'ip6tnl.network', 'ipip.network', 'ipvlan.network', + 'ipvtap.network', 'isatap.network', 'macsec.network', 'macvlan.network', @@ -331,6 +366,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'sit.network', 'vti6.network', 'vti.network', + 'vxlan-test1.network', 'vxlan.network'] def setUp(self): @@ -443,16 +479,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, ' mtu 2004 ') + self.assertRegex(output, ' mtu 2000 ') output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, ' mtu 2000 ') - self.assertTrue(output, 'REORDER_HDR') - self.assertTrue(output, 'LOOSE_BINDING') - self.assertTrue(output, 'GVRP') - self.assertTrue(output, 'MVRP') - self.assertTrue(output, ' id 99 ') + self.assertRegex(output, ' mtu 2000 ') + self.assertRegex(output, 'REORDER_HDR') + self.assertRegex(output, 'LOOSE_BINDING') + self.assertRegex(output, 'GVRP') + self.assertRegex(output, 'MVRP') + self.assertRegex(output, ' id 99 ') output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'test1']).rstrip().decode('utf-8') print(output) @@ -478,11 +514,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, ' mtu 2000 ') + self.assertRegex(output, ' mtu 2000 ') output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvlan99']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, ' mtu 2000 ') + self.assertRegex(output, ' mtu 2000 ') @expectedFailureIfModuleIsNotAvailable('ipvlan') def test_ipvlan(self): @@ -491,6 +527,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertTrue(self.link_exits('ipvlan99')) + @expectedFailureIfModuleIsNotAvailable('ipvtap') + def test_ipvtap(self): + self.copy_unit_to_networkd_unit_path('25-ipvtap.netdev', '11-dummy.netdev', 'ipvtap.network') + self.start_networkd() + + self.assertTrue(self.link_exits('ipvtap99')) + def test_veth(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev') self.start_networkd() @@ -544,24 +587,24 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): subprocess.call('wg') output = subprocess.check_output(['wg', 'show', 'wg99', 'listen-port']).rstrip().decode('utf-8') - self.assertTrue(output, '51820') + self.assertRegex(output, '51820') output = subprocess.check_output(['wg', 'show', 'wg99', 'fwmark']).rstrip().decode('utf-8') - self.assertTrue(output, '0x4d2') + self.assertRegex(output, '0x4d2') output = subprocess.check_output(['wg', 'show', 'wg99', 'allowed-ips']).rstrip().decode('utf-8') - self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48') - self.assertTrue(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128') + self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48') + self.assertRegex(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128') output = subprocess.check_output(['wg', 'show', 'wg99', 'persistent-keepalive']).rstrip().decode('utf-8') - self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20') + self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20') output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints']).rstrip().decode('utf-8') - self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820') + self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.27.3:51820') output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key']).rstrip().decode('utf-8') - self.assertTrue(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=') + self.assertRegex(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=') output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys']).rstrip().decode('utf-8') - self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=') - self.assertTrue(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=') + self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=') + self.assertRegex(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=') output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key']).rstrip().decode('utf-8') - self.assertTrue(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=') + self.assertRegex(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=') def test_geneve(self): self.copy_unit_to_networkd_unit_path('25-geneve.netdev') @@ -571,20 +614,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = subprocess.check_output(['ip', '-d', 'link', 'show', 'geneve99']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, '192.168.22.1') - self.assertTrue(output, '6082') - self.assertTrue(output, 'udpcsum') - self.assertTrue(output, 'udp6zerocsumrx') + self.assertRegex(output, '192.168.22.1') + self.assertRegex(output, '6082') + self.assertRegex(output, 'udpcsum') + self.assertRegex(output, 'udp6zerocsumrx') def test_ipip_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ipip-tunnel.netdev', 'ipip.network', - '25-ipip-tunnel-local-any.netdev', '25-ipip-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('ipiptun99')) - self.assertTrue(self.link_exits('ipiptun98')) - self.assertTrue(self.link_exits('ipiptun97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ipip.network', + '25-ipip-tunnel.netdev', '25-tunnel.network', + '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun99']).rstrip().decode('utf-8') print(output) @@ -597,14 +638,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ipip (?:ipip |)remote any local 192.168.223.238 dev dummy98') def test_gre_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gre-tunnel.netdev', 'gretun.network', - '25-gre-tunnel-local-any.netdev', '25-gre-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('gretun99')) - self.assertTrue(self.link_exits('gretun98')) - self.assertTrue(self.link_exits('gretun97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretun.network', + '25-gre-tunnel.netdev', '25-tunnel.network', + '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun99']).rstrip().decode('utf-8') print(output) @@ -628,9 +667,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertNotRegex(output, 'iseq') self.assertNotRegex(output, 'oseq') + @expectedFailureIf_ip6gre_do_not_support_ipv6ll() def test_ip6gre_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gre-tunnel.netdev', 'ip6gretun.network', - '25-ip6gre-tunnel-local-any.netdev', '25-ip6gre-tunnel-remote-any.netdev') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretun.network', + '25-ip6gre-tunnel.netdev', '25-tunnel.network', + '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) @@ -648,14 +690,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98') - def test_gretap_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretap-tunnel.netdev', 'gretap.network', - '25-gretap-tunnel-local-any.netdev') - self.start_networkd() + # Old kernels may not support IPv6LL address on ip6gre tunnel, and the following test may fails. + self.wait_online(['ip6gretun99:routable', 'ip6gretun98:routable', 'ip6gretun97:routable', 'dummy98:degraded']) - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('gretap99')) - self.assertTrue(self.link_exits('gretap98')) + def test_gretap_tunnel(self): + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretap.network', + '25-gretap-tunnel.netdev', '25-tunnel.network', + '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') + self.start_networkd(0) + self.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap99']).rstrip().decode('utf-8') print(output) @@ -673,13 +716,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'oseq') def test_ip6gretap_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gretap-tunnel.netdev', 'ip6gretap.network', - '25-ip6gretap-tunnel-local-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('ip6gretap99')) - self.assertTrue(self.link_exits('ip6gretap98')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretap.network', + '25-ip6gretap-tunnel.netdev', '25-tunnel.network', + '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') + self.start_networkd(0) + self.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap99']).rstrip().decode('utf-8') print(output) @@ -689,14 +730,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98') def test_vti_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti-tunnel.netdev', 'vti.network', - '25-vti-tunnel-local-any.netdev', '25-vti-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('vtitun99')) - self.assertTrue(self.link_exits('vtitun98')) - self.assertTrue(self.link_exits('vtitun97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti.network', + '25-vti-tunnel.netdev', '25-tunnel.network', + '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun99']).rstrip().decode('utf-8') print(output) @@ -709,14 +748,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98') def test_vti6_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti6-tunnel.netdev', 'vti6.network', - '25-vti6-tunnel-local-any.netdev', '25-vti6-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('vti6tun99')) - self.assertTrue(self.link_exits('vti6tun98')) - self.assertTrue(self.link_exits('vti6tun97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti6.network', + '25-vti6-tunnel.netdev', '25-tunnel.network', + '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun99']).rstrip().decode('utf-8') print(output) @@ -729,14 +766,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'vti6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') def test_ip6tnl_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6tnl-tunnel.netdev', 'ip6tnl.network', - '25-ip6tnl-tunnel-local-any.netdev', '25-ip6tnl-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('ip6tnl99')) - self.assertTrue(self.link_exits('ip6tnl98')) - self.assertTrue(self.link_exits('ip6tnl97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6tnl.network', + '25-ip6tnl-tunnel.netdev', '25-tunnel.network', + '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl99']).rstrip().decode('utf-8') print(output) @@ -749,15 +784,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ip6tnl ip6ip6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') def test_sit_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-sit-tunnel.netdev', 'sit.network', - '25-sit-tunnel-local-any.netdev', - '25-sit-tunnel-remote-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('sittun99')) - self.assertTrue(self.link_exits('sittun98')) - self.assertTrue(self.link_exits('sittun97')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network', + '25-sit-tunnel.netdev', '25-tunnel.network', + '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network', + '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + self.start_networkd(0) + self.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99']).rstrip().decode('utf-8') print(output) @@ -770,8 +802,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, "sit (?:ip6ip |)remote any local 10.65.223.238 dev dummy98") def test_isatap_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-isatap-tunnel.netdev', 'isatap.network') - self.start_networkd() + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'isatap.network', + '25-isatap-tunnel.netdev', '25-tunnel.network') + self.start_networkd(0) + self.wait_online(['isataptun99:routable', 'dummy98:degraded']) self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('isataptun99')) @@ -781,11 +815,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, "isatap ") def test_6rd_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-6rd-tunnel.netdev', '6rd.network') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('sittun99')) + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '6rd.network', + '25-6rd-tunnel.netdev', '25-tunnel.network') + self.start_networkd(0) + self.wait_online(['sittun99:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99']).rstrip().decode('utf-8') print(output) @@ -794,12 +827,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfERSPANModuleIsNotAvailable() def test_erspan_tunnel(self): self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'erspan.network', - '25-erspan-tunnel.netdev', '25-erspan-tunnel-local-any.netdev') - self.start_networkd() - - self.assertTrue(self.link_exits('dummy98')) - self.assertTrue(self.link_exits('erspan99')) - self.assertTrue(self.link_exits('erspan98')) + '25-erspan-tunnel.netdev', '25-tunnel.network', + '25-erspan-tunnel-local-any.netdev', '25-tunnel-local-any.network') + self.start_networkd(0) + self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan99']).rstrip().decode('utf-8') print(output) @@ -861,14 +892,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): subprocess.call(['ip', 'fou', 'del', 'port', '55556']) def test_vxlan(self): - self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network', '11-dummy.netdev') - self.start_networkd() + self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network', + '11-dummy.netdev', 'vxlan-test1.network') + self.start_networkd(0) - self.assertTrue(self.link_exits('vxlan99')) + self.wait_online(['test1:degraded', 'vxlan99:degraded']) output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vxlan99']).rstrip().decode('utf-8') print(output) - self.assertRegex(output, "999") + self.assertRegex(output, '999') self.assertRegex(output, '5555') self.assertRegex(output, 'l2miss') self.assertRegex(output, 'l3miss') @@ -879,6 +911,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'remcsumrx') self.assertRegex(output, 'gbp') + output = subprocess.check_output(['bridge', 'fdb', 'show', 'dev', 'vxlan99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '00:11:22:33:44:55 dst 10.0.0.5 self permanent') + self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent') + self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 self permanent') + def test_macsec(self): self.copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key', 'macsec.network', '12-dummy.netdev') @@ -1195,12 +1233,14 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58') self.assertRegex(output, '149.10.124.64 proto static scope link') + self.assertRegex(output, '169.254.0.0/16 proto static scope link metric 2048') self.assertRegex(output, '192.168.1.1 proto static initcwnd 20') self.assertRegex(output, '192.168.1.2 proto static initrwnd 30') output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8') self.assertRegex(output, 'default via 149.10.125.65 proto static onlink') self.assertRegex(output, 'default via 149.10.124.64 proto static') + self.assertRegex(output, 'default proto static') output = subprocess.check_output(['ip', 'route', 'show', 'type', 'blackhole']).rstrip().decode('utf-8') print(output) @@ -1872,6 +1912,8 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): 'dhcp-client-route-metric.network', 'dhcp-client-route-table.network', 'dhcp-client-vrf.network', + 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network', + 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network', 'dhcp-client.network', 'dhcp-server-veth-peer.network', 'dhcp-v4-server-veth-peer.network', @@ -1890,17 +1932,22 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_ipv6_only(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network') - self.start_networkd() - - self.assertTrue(self.link_exits('veth99')) + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8') print(output) self.assertRegex(output, '2600::') self.assertNotRegex(output, '192.168.5') + # Confirm that ipv6 token is not set in the kernel + output = subprocess.check_output(['ip', 'token', 'show', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'token :: dev veth99') + def test_dhcp_client_ipv4_only(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only-ipv6-disabled.network') self.start_networkd() @@ -2049,7 +2096,7 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '192.168.5.*') - # Stoping dnsmasq as networkd won't be allowed to renew the DHCP lease. + # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease. self.stop_dnsmasq(dnsmasq_pid_file) # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 @@ -2168,6 +2215,61 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'onlink') + def test_dhcp_client_with_ipv4ll_fallback_with_dhcp_server(self): + self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', + 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network') + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) + self.start_dnsmasq(lease_time='2m') + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet6 .* scope link') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet .* scope link') + + print('Wait for the dynamic address to be expired') + time.sleep(130) + + output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet6 .* scope link') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet .* scope link') + + self.search_words_in_dnsmasq_log('DHCPOFFER', show_all=True) + + def test_dhcp_client_with_ipv4ll_fallback_without_dhcp_server(self): + self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', + 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network') + self.start_networkd(0) + self.wait_online(['veth99:degraded', 'veth-peer:routable']) + + output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet6 .* scope link') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8') + self.assertNotRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet .* scope link') + if __name__ == '__main__': unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3)) diff --git a/test/udev-test.pl b/test/udev-test.pl index 122359e377..b37b2e975f 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -279,7 +279,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" EOF }, { - desc => "sustitution of sysfs value (%s{file})", + desc => "substitution of sysfs value (%s{file})", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_name => "disk-ATA-sda", rules => <<EOF diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build index b94a9d0f47..f14b4fc6df 100644 --- a/tmpfiles.d/meson.build +++ b/tmpfiles.d/meson.build @@ -4,7 +4,7 @@ enable_tmpfiles = conf.get('ENABLE_TMPFILES') == 1 tmpfiles = [['home.conf', ''], ['journal-nocow.conf', ''], - ['systemd-nologin.conf', ''], + ['systemd-nologin.conf', 'HAVE_PAM'], ['systemd-nspawn.conf', 'ENABLE_MACHINED'], ['systemd-tmp.conf', ''], ['portables.conf', 'ENABLE_PORTABLED'], diff --git a/tools/check-directives.sh b/tools/check-directives.sh index e8c6e60040..46f770222f 100755 --- a/tools/check-directives.sh +++ b/tools/check-directives.sh @@ -10,17 +10,23 @@ function generate_directives() { } ret=0 -if [[ $(generate_directives src/network/networkd-network-gperf.gperf | wc -l) -ne $(wc -l <test/fuzz/fuzz-network-parser/directives.network) ]]; then +if ! diff \ + <(generate_directives src/network/networkd-network-gperf.gperf | sort) \ + <(cat test/fuzz/fuzz-network-parser/directives.network | sort); then echo "Looks like test/fuzz/fuzz-network-parser/directives.network hasn't been updated" ret=1 fi -if [[ $(generate_directives src/network/netdev/netdev-gperf.gperf | wc -l) -ne $(wc -l <test/fuzz/fuzz-netdev-parser/directives.netdev) ]]; then +if ! diff \ + <(generate_directives src/network/netdev/netdev-gperf.gperf | sort) \ + <(cat test/fuzz/fuzz-netdev-parser/directives.netdev | sort); then echo "Looks like test/fuzz/fuzz-netdev-parser/directives.netdev hasn't been updated" ret=1 fi -if [[ $(generate_directives src/udev/net/link-config-gperf.gperf | wc -l) -ne $(wc -l <test/fuzz/fuzz-link-parser/directives.link) ]]; then +if ! diff \ + <(generate_directives src/udev/net/link-config-gperf.gperf | sort) \ + <(cat test/fuzz/fuzz-link-parser/directives.link | sort) ; then echo "Looks like test/fuzz/fuzz-link-parser/directives.link hasn't been updated" ret=1 fi diff --git a/tools/coverity.sh b/tools/coverity.sh index 5842eeb81f..e32f5b3993 100755 --- a/tools/coverity.sh +++ b/tools/coverity.sh @@ -142,7 +142,7 @@ _upload() $UPLOAD_URL) printf "\033[33;1mThe response is\033[0m\n%s\n" "$response" status_code=$(echo "$response" | sed -n '$p') - # Coverity Scan used to respond with 201 on successfully receieving analysis results. + # Coverity Scan used to respond with 201 on successfully receiving analysis results. # Now for some reason it sends 200 and may change back in the foreseeable future. # See https://github.com/pmem/pmdk/commit/7b103fd2dd54b2e5974f71fb65c81ab3713c12c5 if [ "$status_code" != "200" ]; then @@ -151,7 +151,7 @@ _upload() exit 1 fi - echo -e "\n\033[33;1mCoverity Scan Analysis completed succesfully.\033[0m" + echo -e "\n\033[33;1mCoverity Scan Analysis completed successfully.\033[0m" exit 0 } diff --git a/travis-ci/Dockerfile b/travis-ci/Dockerfile index 71dd07afe8..daf0ea9c64 100644 --- a/travis-ci/Dockerfile +++ b/travis-ci/Dockerfile @@ -1,5 +1,5 @@ ## Create Dockerfile that builds container suitable for systemd build -## This container runs as non-root user by deafult +## This container runs as non-root user by default FROM fedora:27 diff --git a/travis-ci/managers/fuzzbuzz.sh b/travis-ci/managers/fuzzbuzz.sh new file mode 100755 index 0000000000..0a296f75f8 --- /dev/null +++ b/travis-ci/managers/fuzzbuzz.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e +set -x +set -u + +REPO_ROOT=${REPO_ROOT:-$(pwd)} + +cd $REPO_ROOT +wget https://app.fuzzbuzz.io/releases/cli/latest/linux/fuzzbuzz +chmod +x fuzzbuzz +./fuzzbuzz validate +./fuzzbuzz target test fuzz-unit-file --all + +git clone https://github.com/google/oss-fuzz /tmp/oss-fuzz +cd /tmp/oss-fuzz +sudo ./infra/helper.py pull_images + +# helper.py is wrapped in script to trick it into thinking it's "interactive" +# See https://github.com/systemd/systemd/pull/12542#issuecomment-491563572 +sudo script -e -c "./infra/helper.py build_fuzzers --clean --sanitizer=memory systemd $REPO_ROOT" +sudo script -e -c "./infra/helper.py check_build --sanitizer=memory systemd" diff --git a/travis-ci/managers/xenial.sh b/travis-ci/managers/xenial.sh index 4822bdd4b7..33b40e63c5 100755 --- a/travis-ci/managers/xenial.sh +++ b/travis-ci/managers/xenial.sh @@ -2,21 +2,25 @@ set -e set -x +bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list" apt-get update apt-get build-dep systemd -y apt-get install -y util-linux libmount-dev libblkid-dev liblzma-dev libqrencode-dev libmicrohttpd-dev iptables-dev liblz4-dev libcurl4-gnutls-dev unifont itstool kbd cryptsetup-bin net-tools isc-dhcp-client iputils-ping strace qemu-system-x86 linux-image-virtual mount libgpg-error-dev libxkbcommon-dev python-lxml python3-lxml python3-pip libcap-dev apt-get install -y gettext python3-evdev python3-pyparsing libmount-dev python3-setuptools ninja-build pip3 install meson -cd $REPO_ROOT +cd ${REPO_ROOT:-$(pwd)} sed -i 's/2\.30/2.27/' meson.build meson --werror -Db_sanitize=address,undefined -Dsplit-usr=true build ninja -v -C build -make -C test/TEST-01-BASIC clean setup run TEST_NO_QEMU=yes NSPAWN_ARGUMENTS=--keep-unit RUN_IN_UNPRIVILEGED_CONTAINER=no + +make -C test/TEST-01-BASIC clean setup run NSPAWN_TIMEOUT=600 TEST_NO_QEMU=yes NSPAWN_ARGUMENTS=--keep-unit RUN_IN_UNPRIVILEGED_CONTAINER=no # Now that we're more or less sure that ASan isn't going to crash systemd and cause a kernel panic # let's also run the test with QEMU to cover udevd, sysctl and everything else that isn't run # in containers. -make -C test/TEST-01-BASIC clean setup run TEST_NO_NSPAWN=yes + +# This should be turned on once `journalctl --flush` isn't flaky any more +#make -C test/TEST-01-BASIC clean setup run QEMU_TIMEOUT=900 TEST_NO_NSPAWN=yes diff --git a/units/debug-shell.service.in b/units/debug-shell.service.in index 1127e68b63..9f3868e106 100644 --- a/units/debug-shell.service.in +++ b/units/debug-shell.service.in @@ -9,7 +9,6 @@ [Unit] Description=Early root shell on @DEBUGTTY@ FOR DEBUGGING ONLY -Documentation=man:sushell(8) Documentation=man:systemd-debug-generator(8) DefaultDependencies=no IgnoreOnIsolate=yes diff --git a/units/systemd-journal-flush.service.in b/units/systemd-journal-flush.service.in index bacfe51d6f..29b006cba5 100644 --- a/units/systemd-journal-flush.service.in +++ b/units/systemd-journal-flush.service.in @@ -18,6 +18,7 @@ RequiresMountsFor=/var/log/journal [Service] ExecStart=@rootbindir@/journalctl --flush +ExecStop=@rootbindir@/journalctl --smart-relinquish-var Type=oneshot RemainAfterExit=yes TimeoutSec=90s |