[Distribution]
Distribution=fedora
-Release=29
+Release=30
[Output]
Format=raw_btrfs
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:
CHANGES WITH 243 in spe:
+ * Previously, filters defined with SystemCallFilter= would have the
+ effect that an calling an offending system call would terminate the
+ calling thread. This behaviour never made much sense, since killing
+ individual threads of unexpecting processes is likely to create more
+ problems than it solves. With this release the default action changed
+ from killing the thread to killing the whole process. For this to
+ work correctly both a kernel version (>= 4.14) and a libseccomp
+ version (>= 2.4.0) supporting this new seccomp action is required. If
+ an older kernel or libseccomp is used the old behaviour continues to
+ be used. This change does not affect any services that have no system
+ call filters defined, or that use SystemCallErrorNumber= (and thus
+ see EPERM or another error instead of being killed when calling an
+ offending system call). Note that systemd documentation always
+ claimed that the whole process is killed. With this change behaviour
+ is thus adjusted to match the documentation.
+
* The "kernel.pid_max" sysctl is now bumped to 4194304 by default,
i.e. the full 22bit range the kernel allows, up from the old 16bit
range. This should improve security and robustness a bit, as PID
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.
+
+ * systemd now defaults to the "unified" cgroup hierarchy setup during
+ build-time, i.e. -Ddefault-hierarchy=unified is now the build-time
+ default. Previously, -Ddefault-hierarchy=hybrid was the default. This
+ change reflects the fact that cgroupsv2 support has matured
+ substantially in both systemd and in the kernel, and is clearly the
+ way forward. Downstream production distributions might want to
+ continue to use -Ddefault-hierarchy=hybrid (or even =legacy) for
+ their builds as unfortunately the popular container managers have not
+ caught up with the kernel API changes.
+
+ * Man pages are not built by default anymore (html pages were already
+ disabled by default), to make development builds quicker. When
+ building systemd for a full installation with documentation, meson
+ should be called -Dman=true and/or -Dhtml=true as appropriate. The
+ default was changed based on the assumption that quick one-off or
+ repeated development builds are much more common than full optimized
+ builds for installation, and people need to pass various other
+ options to when doing "proper" builds anyway, so the gain from making
+ development builds quicker is bigger than the one time disruption for
+ packagers.
+
+ Two scripts are created in the *build* directory to generate and
+ preview man and html pages on demand, e.g.:
+
+ build/man/man systemctl
+ build/man/html systemd.index
+
+ * /usr/sbin/halt.local is no longer supported. Implementation in
+ distributions was inconsistent and it seems this functionality was
+ very rarely used.
+
+ To replace this functionality, users should:
+ - either define a new unit and make it a dependency of final.target
+ (systemctl add-wants final.target my-halt-local.service)
+ - or move the shutdown script to /usr/lib/systemd/system-shutdown/
+ and ensure that it accepts "halt", "poweroff", "reboot", and
+ "kexec" as an argument, see the description in systemd-shutdown(8).
+
+ * When a [Match] section in .link or .network file is empty (contains
+ no match patterns), a warning will be emitted. Please add any "match
+ all" pattern instead, e.g. OriginalName=* or Name=* if case all
+ interfaces should really be matched.
+
…
CHANGES WITH 242:
<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
manager or system manager can be always set. It would be better to reject
them when parsing config.
+* busctl --user call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager GetUnitProcesses "s" run-rbff1b85427b34ba3adf864281aeda8e7.service
+Failed to set address: No such file or directory
+
+ → improve error message
+
External:
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
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
--- /dev/null
+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'
# 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
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
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
@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
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
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
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
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
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
La postazione @SEAT_ID@ è stata rimossa e non è più disponibile.
+# Subject: Time Change
-- c7a787079b354eaaa9e77b371893cd27
Subject: Cambio d'orario
Defined-By: systemd
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
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.
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
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
Il sistema è uscito dallo stato di pausa @SLEEP@.
+# Subject: System shutdown initiated
-- 98268866d1d54a499c4e98921d93bc40
Subject: Il sistema è in fase di spegnimento
Defined-By: systemd
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
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%
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
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
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
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
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
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
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
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
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@.
@@
constant s;
@@
+(
+#define STRLEN
+&
- sizeof(s)-1
+ STRLEN(s)
+)
@@
constant s;
@@
@@
@@
+(
+#define DEBUG_LOGGING
+&
- _unlikely_(log_get_max_level() >= LOG_DEBUG)
+ DEBUG_LOGGING
+)
@@
@@
+(
+#define DEBUG_LOGGING
+&
- log_get_max_level() >= LOG_DEBUG
+ DEBUG_LOGGING
+)
@@
+/* 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)
@@
+/* 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)
@@
+/* 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)
+)
@@
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)
+)
@@
+/* 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)
+)
@@
expression a, b;
@@
+(
+#define memzero
+&
- memset(a, 0, b)
+ memzero(a, b)
+)
@@
expression a, b;
@@
+(
+#define memzero
+&
- bzero(a, b)
+ memzero(a, b)
+)
@@
-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);
@@
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)
+)
#!/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"
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
--- /dev/null
+@@
+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);
@@
+/* 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);
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;
--- /dev/null
+/* 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
- 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;
of active alerts can be found
[here](https://lgtm.com/projects/g/systemd/systemd/alerts/?mode=list).
+15. Each PR is automatically tested with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
+ and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
+ See [Testing systemd using sanitizers](https://systemd.io/TESTING_WITH_SANITIZERS)
+ for more information.
+
Access to Coverity and oss-fuzz reports is limited. Please reach out to the
maintainers if you need access.
* `-Dsysvinit-path=`
* `-Dsysvrcnd-path=`
* `-Drc-local=`
- * `-Dhalt-local=`
* `-Dloadkeys-path=`
* `-Dsetfont-path=`
* `-Dtty-gid=`
--- /dev/null
+---
+title: Testing systemd using sanitizers
+---
+
+# Testing systemd using sanitizers
+
+To catch the *nastier* kind of bugs, you can run your code with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
+and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
+This is mostly done automagically by various CI systems for each PR, but you may
+want to do it locally as well. The process slightly varies depending on the
+compiler you want to use and which part of the test suite you want to run.
+
+## gcc
+gcc compiles in sanitizer libraries dynamically by default, so you need to get
+the shared libraries first - on Fedora these are shipped as a separate packages
+(`libasan` for Address Sanitizer and `libubsan` for Undefined Behavior Sanitizer).
+
+The compilation itself is then a matter of simply adding `-Db_sanitize=address,undefined`
+to `meson`. That's it - following executions of `meson test` and integrations tests
+under `test/` subdirectory will run with sanitizers enabled. However, to get
+truly useful results, you should tweak the runtime configuration of respective
+sanitizers; e.g. in systemd we set the following environment variables:
+
+```bash
+ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
+UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
+```
+## clang
+In case of clang things are somewhat different - the sanitizer libraries are
+compiled in statically by default. This is not an issue if you plan to run
+only the unit tests, but for integration tests you'll need to convince clang
+to use the dynamic versions of sanitizer libraries.
+
+First of all, pass `-shared-libsan` to both `clang` and `clang++`:
+
+```bash
+CFLAGS=-shared-libasan
+CXXFLAGS=-shared-libasan
+```
+
+The `CXXFLAGS` are necessary for `src/libsystemd/sd-bus/test-bus-vtable-cc.c`. Compilation
+is then the same as in case of gcc, simply add `-Db_sanitize=address,undefined`
+to the `meson` call and use the same environment variables for runtime configuration.
+
+```bash
+ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
+UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
+```
+
+After this, you'll probably notice that all compiled binaries complain about
+missing `libclang_rt.asan*` library. To fix this, you have to install clang's
+runtime libraries, usually shipped in the `compiler-rt` package. As these libraries
+are installed in a non-standard location (non-standard for `ldconfig`), you'll
+need to manually direct binaries to the respective runtime libraries.
+
+```
+# Optionally locate the respective runtime DSO
+$ ldd build/systemd | grep libclang_rt.asan
+ libclang_rt.asan-x86_64.so => not found
+ libclang_rt.asan-x86_64.so => not found
+$ find /usr/lib* /usr/local/lib* -type f -name libclang_rt.asan-x86_64.so 2>/dev/null
+/usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so
+
+# Set the LD_LIBRARY_PATH accordingly
+export LD_LIBRARY_PATH=/usr/lib64/clang/7.0.1/lib/
+
+# If the path is correct, the "not found" message should change to an actual path
+$ ldd build/systemd | grep libclang_rt.asan
+ libclang_rt.asan-x86_64.so => /usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so (0x00007fa9752fc000)
+```
+
+This should help binaries to correctly find necessary sanitizer DSOs.
+
+Also, to make the reports useful, `llvm-symbolizer` tool is required (usually
+part of the `llvm` package).
+
+## Background notes
+The reason why you need to force dynamic linking in case of `clang` is that some
+applications make use of `libsystemd`, which is compiled with sanitizers as well.
+However, if a *standard* (uninstrumented) application loads an instrumented library,
+it will immediately fail due to unresolved symbols. To fix/workaround this, you
+need to pre-load the ASan DSO using `LD_PRELOAD=/path/to/asan/dso`, which will
+make things work as expected in most cases. This will, obviously, not work with
+statically linked sanitizer libraries.
+
+These shenanigans are performed automatically when running the integration test
+suite (i.e. `test/TEST-??-*`) and are located in `test/test-functions` (mainly,
+but not only, in the `create_asan_wrapper` function).
✓ CPUQuota=
✓ CPUQuotaPeriodSec=
✓ MemoryAccounting=
+✓ DefaultMemoryMin=
✓ MemoryMin=
✓ DefaultMemoryLow=
✓ MemoryLow=
| 65535 | 16bit `(uid_t) -1` | Linux | |
| 65536…524287 | Unused | | |
| 524288…1879048191 | Container UID ranges | `systemd` | `nss-mymachines` |
-| 1879048192…4294967294 | Unused | | |
+| 1879048191…2147483647 | Unused | | |
+| 2147483648…4294967294 | HIC SVNT LEONES | | |
| 4294967295 | 32bit `(uid_t) -1` | Linux | |
Note that "Unused" in the table above doesn't meant that these ranges are
`systemd`. There might very well be other packages that allocate from these
ranges.
+Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled
+with care. Various programs (including kernel file systems, see `devpts`) have
+trouble with UIDs outside of the signed 32bit range, i.e any UIDs equal to or
+above 2147483648. It is thus strongly recommended to stay away from this range
+in order to avoid complications. This range should be considered reserved for
+future, special purposes.
+
## Notes on resolvability of user and group names
User names, UIDs, group names and GIDs don't have to be resolvable using NSS
--- /dev/null
+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
# 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
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
###########################################################
# 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
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)
###########################################################
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
sensor:modalias:acpi:BOSC0200*:dmi:*:svnLINX*:pnLINX1010B:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
-# Linx 12X64
-sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12X64:*
+# Linx 12X64 and 12V64
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12*64:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
#########################################
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
#########################################
<varlistentry>
<term><option>-p</option></term>
<term><option>--print-esp-path</option></term>
- <listitem><para>This option modifies the behaviour of <command>status</command>. Prints only the
- path to the EFI System Partition (ESP) to standard output and exits.</para></listitem>
+ <listitem><para>This option modifies the behaviour of <command>status</command>. Only prints the path
+ to the EFI System Partition (ESP) to standard output and exits.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><option>-x</option></term>
<term><option>--print-boot-path</option></term>
- <listitem><para>This option modifies the behaviour of <command>status</command>. Prints only the
- path to the Extended Boot Loader partition if it exists, and the path to the ESP otherwise to
- standard output and exit. This command is useful to determine where to place boot loader entries, as
- they are preferably placed in the Extended Boot Loader partition if it exists and in the ESP
- otherwise.</para></listitem>
+ <listitem><para>This option modifies the behaviour of <command>status</command>. Only prints the path
+ to the Extended Boot Loader partition if it exists, and the path to the ESP otherwise to standard
+ output and exit. This command is useful to determine where to place boot loader entries, as they are
+ preferably placed in the Extended Boot Loader partition if it exists and in the ESP otherwise.
+ </para></listitem>
</varlistentry>
<varlistentry>
--- /dev/null
+#!/bin/sh
+set -e
+
+if [ -z "$1" ]; then
+ echo "Use: $0 page-name (with no section suffix)"
+ exit 1
+fi
+
+target="man/$1.html"
+ninja -C "@BUILD_ROOT@" "$target"
+set -x
+exec xdg-open build/"$target"
<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>
--- /dev/null
+#!/bin/sh
+set -e
+
+if [ -z "$1" ]; then
+ echo "Use: $0 page-name (with no section suffix)"
+ exit 1
+fi
+
+target=$(ninja -C "@BUILD_ROOT@" -t query man/man | grep -E -m1 "man/$1\.[0-9]$" | awk '{print $2}')
+if [ -z "$target" ]; then
+ echo "Cannot find page $1"
+ exit 1
+fi
+ninja -C "@BUILD_ROOT@" "$target"
+exec man build/"$target"
'mv t @0@/rules/meson.build'.format(meson.current_source_dir())],
depend_files : custom_entities_ent)
endif
+
+############################################################
+
+configure_file(
+ input : 'man.in',
+ output : 'man',
+ configuration : substs)
+
+configure_file(
+ input : 'html.in',
+ output : 'html',
+ configuration : substs)
['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',
['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', [], ''],
<refsect1>
<title>Description</title>
- <para><function>sd_bus_request_name()</function> requests a well-known service name on a bus. It takes a bus
- connection, a valid bus name and a flags parameter. The flags parameter is a combination of the following
- flags:</para>
+ <para><function>sd_bus_request_name()</function> requests a well-known service name on a bus. It takes a
+ bus connection, a valid bus name, and a flags parameter. The flags parameter is a combination of zero or
+ more of the following flags:</para>
<variablelist>
<varlistentry>
<varlistentry>
<term><constant>SD_BUS_NAME_REPLACE_EXISTING</constant></term>
- <listitem><para>Take over the name if it is already acquired by another peer, and that other peer has permitted
- takeover by setting <constant>SD_BUS_NAME_ALLOW_REPLACEMENT</constant> while acquiring it.</para></listitem>
+ <listitem><para>Take over the name if it was already acquired by another peer, and that other peer
+ has permitted takeover by setting <constant>SD_BUS_NAME_ALLOW_REPLACEMENT</constant> while acquiring
+ it.</para></listitem>
</varlistentry>
<varlistentry>
<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>
<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>
<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>
<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>
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" />
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">calendar</arg>
- <arg choice="plain" rep="repeat"><replaceable>SPECS</replaceable></arg>
+ <arg choice="plain" rep="repeat"><replaceable>SPEC</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-analyze</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">timestamp</arg>
+ <arg choice="plain" rep="repeat"><replaceable>TIMESTAMP</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. By
default, only the next time the calendar expression will elapse is shown; use
<option>--iterations=</option> to show the specified number of next times the expression
- elapses.</para>
+ elapses. Each time the expression elapses forms a timestamp, see the <command>timestamp</command>
+ verb below.</para>
<example>
<title>Show leap days in the near future</title>
</example>
</refsect2>
+ <refsect2>
+ <title><command>systemd-analyze timestamp <replaceable>TIMESTAMP</replaceable>...</command></title>
+
+ <para>This command parses a timestamp (i.e. a single point in time) and outputs the normalized form and
+ the difference between this timestamp and now. The timestamp should adhere to the syntax documented in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ section "PARSING TIMESTAMPS".</para>
+
+ <example>
+ <title>Show parsing of timestamps</title>
+
+ <programlisting>$ systemd-analyze timestamp yesterday now tomorrow
+ Original form: yesterday
+Normalized form: Mon 2019-05-20 00:00:00 CEST
+ (in UTC): Sun 2019-05-19 22:00:00 UTC
+ UNIX seconds: @15583032000
+ From now: 1 day 9h ago
+
+ Original form: now
+Normalized form: Tue 2019-05-21 09:48:39 CEST
+ (in UTC): Tue 2019-05-21 07:48:39 UTC
+ UNIX seconds: @1558424919.659757
+ From now: 43us ago
+
+ Original form: tomorrow
+Normalized form: Wed 2019-05-22 00:00:00 CEST
+ (in UTC): Tue 2019-05-21 22:00:00 UTC
+ UNIX seconds: @15584760000
+ From now: 14h left
+</programlisting>
+ </example>
+ </refsect2>
+
<refsect2>
<title><command>systemd-analyze timespan <replaceable>EXPRESSION</replaceable>...</command></title>
- <para>This command parses a time span and outputs the normalized form and the equivalent value in
- microseconds. The time span should adhere to the same syntax documented in
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
- Values without associated magnitudes are parsed as seconds.</para>
+ <para>This command parses a time span (i.e. a difference between two timestamps) and outputs the
+ normalized form and the equivalent value in microseconds. The time span should adhere to the syntax
+ documented in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ section "PARSING TIME SPANS". Values without units are parsed as seconds.</para>
<example>
<title>Show parsing of timespans</title>
</varlistentry>
<varlistentry>
- <term><option>--bind-device=</option></term>
+ <term><option>--bind-device</option></term>
- <listitem><para>Takes a boolean argument, defaults to off. This option only has an effect in automount mode,
- and controls whether the automount unit shall be bound to the backing device's lifetime. If enabled, the
- automount point will be removed automatically when the backing device vanishes. If disabled the automount point
+ <listitem><para>This option only has an effect in automount mode,
+ and controls whether the automount unit shall be bound to the backing device's lifetime. If set, the
+ automount point will be removed automatically when the backing device vanishes. By default the automount point
stays around, and subsequent accesses will block until backing device is replugged. This option has no effect
in case of non-device mounts, such as network or virtual file system mounts.</para>
<?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" [
-<!ENTITY fedora_latest_version "28">
-<!ENTITY fedora_cloud_release "1.1">
+<!ENTITY fedora_latest_version "30">
+<!ENTITY fedora_cloud_release "1.2">
]>
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<programlisting># machinectl pull-raw --verify=no \
https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz
-# systemd-nspawn -M Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw</programlisting>
+# systemd-nspawn -M Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64</programlisting>
<para>This downloads an image using
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<refnamediv>
<refname>systemd-rc-local-generator</refname>
- <refpurpose>Compatibility generator for starting <filename>/etc/rc.local</filename> and <filename>/usr/sbin/halt.local</filename> during boot and shutdown</refpurpose>
+ <refpurpose>Compatibility generator for starting <filename>/etc/rc.local</filename> during boot</refpurpose>
</refnamediv>
<refsynopsisdiv>
script is run after <filename>network.target</filename>, but in parallel with most other regular system
services.</para>
- <para><filename>systemd-rc-local-generator</filename> also checks whether <filename>/usr/sbin/halt.local</filename>
- exists and is executable, and if it is pulls the <filename>halt-local.service</filename> unit into the shutdown
- process. This unit is responsible for running this script during later shutdown.</para>
-
- <para>Support for both <filename>/etc/rc.local</filename> and <filename>/usr/sbin/halt.local</filename> is provided
+ <para>Support for <filename>/etc/rc.local</filename> is provided
for compatibility with specific System V systems only. However, it is strongly recommended to avoid making use of
- these scripts today, and instead provide proper unit files with appropriate dependencies for any scripts to run
- during the boot or shutdown processes.</para>
+ this script today, and instead provide proper unit files with appropriate dependencies for any scripts to run
+ during the boot process.</para>
<para><filename>systemd-rc-local-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
are searched for a matching file and the file found that has the highest priority is
executed.</para>
+
+ <para>System services (<filename>systemd-tmpfiles-setup.service</filename>,
+ <filename>systemd-tmpfiles-setup-dev.service</filename>,
+ <filename>systemd-tmpfiles-clean.service</filename>) invoke <command>systemd-tmpfiles</command> to create
+ system files and to perform system wide cleanup. Those services read administrator-controlled
+ configuration files in <filename>tmpfiles.d/</filename> directories. User services
+ (<filename>systemd-tmpfiles-setup.service</filename>,
+ <filename>systemd-tmpfiles-clean.service</filename>) also invoke <command>systemd-tmpfiles</command>, but
+ it reads a separate set of files, which includes user-controlled files under
+ <filename>~/.config/user-tmpfiles.d/</filename> and <filename>~/.local/share/user-tmpfiles.d/</filename>,
+ and administrator-controller files under <filename>/usr/share/user-tmpfiles.d/</filename>. Users may use
+ this to create and clean up files under their control, but the system instance performs global cleanup
+ and is not influenced by user configuration. Note that this means a time-based cleanup configured in the
+ system instance, such as the one typically configured for <filename>/tmp</filename>, will thus also
+ affect files created by the user instance if they are placed in <filename>/tmp</filename>, even if the
+ user instance's time-based cleanup is turned off.</para>
</refsect1>
<refsect1>
</variablelist>
<para>It is possible to combine <option>--create</option>, <option>--clean</option>, and <option>--remove</option>
- in one invocation (in which case removal and clean-up are executed before creation of new files). For example,
+ in one invocation (in which case removal and cleanup are executed before creation of new files). For example,
during boot the following command line is executed to ensure that all temporary and volatile directories are
removed and created according to the configuration file:</para>
<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
</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>
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>
<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>
<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>
<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>
<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>
mount options are understood by systemd which influence how
dependencies are created for mount points. systemd will create a
dependency of type <varname>Wants=</varname> or
- <option>Requires</option> (see option <option>nofail</option>
+ <option>Requires=</option> (see option <option>nofail</option>
below), from either <filename>local-fs.target</filename> or
<filename>remote-fs.target</filename>, depending whether the file
system is local or remote.</para>
--- /dev/null
+<?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>
<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>
</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
<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>
<listitem>
<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>
<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>
VXLAN Group Policy </ulink> document. Defaults to false.</para>
</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>
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>
<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>
<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>
<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>
for delivery to the real destination. This option is mandatory.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PeerPort=</varname></term>
+ <listitem>
+ <para>Specifies the peer port number. Defaults to unset. Note that when peer port is set <literal>Peer=</literal> address is mandotory.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>Protocol=</varname></term>
<listitem>
<varname>Encapsulation=GenericUDPEncapsulation</varname>, this must not be specified.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Peer=</varname></term>
+ <listitem>
+ <para>Configures peer IP address. Note that when peer address is set <literal>PeerPort=</literal> is mandotory.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Local=</varname></term>
+ <listitem>
+ <para>Configures local IP address.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
keys:</para>
<variablelist class='network-directives'>
- <varlistentry>
- <term><varname>OneQueue=</varname></term>
- <listitem><para>Takes a boolean. Configures whether
- all packets are queued at the device (enabled), or a fixed
- number of packets are queued at the device and the rest at the
- <literal>qdisc</literal>. Defaults to
- <literal>no</literal>.</para>
- </listitem>
- </varlistentry>
<varlistentry>
<term><varname>MultiQueue=</varname></term>
<listitem><para>Takes a boolean. Configures whether
<varlistentry>
<term><varname>VNetHeader=</varname></term>
<listitem><para>Takes a boolean. Configures
- IFF_VNET_HDR flag for a tap device. It allows sending
+ IFF_VNET_HDR flag for a tun or tap device. It allows sending
and receiving larger Generic Segmentation Offload (GSO)
packets. This may increase throughput significantly.
Defaults to
</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>
<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>
</para>
</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>
</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>TTLPropagate=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When true enables TTL propagation at Label Switched Path (LSP) egress.
+ When unset, the kernel's default will be used.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>MTUBytes=</varname></term>
<listitem>
</listitem>
</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>
</listitem>
</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>
</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>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>BlackList=</varname></term>
+ <listitem>
+ <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router advertisements in the list are ignored.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
When unset, the kernel's default will be used.</para>
</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>
key is mandatory.</para>
</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>
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>
<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>
<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>
which configure resource control settings for the processes of the
socket.</para>
- <para>For each socket file, a matching service file must exist,
+ <para>For each socket unit, a matching service unit must exist,
describing the service to start on incoming traffic on the socket
(see
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information about .service files). The name of the
+ for more information about .service units). The name of the
.service unit is by default the same as the name of the .socket
unit, but can be altered with the <option>Service=</option> option
described below. Depending on the setting of the
socket file <filename>foo.socket</filename> needs a matching
service <filename>foo.service</filename> if
<option>Accept=no</option> is set. If
- <option>Accept=yes</option> is set, a service template file
+ <option>Accept=yes</option> is set, a service template
<filename>foo@.service</filename> must exist from which services
are instantiated for each incoming connection.</para>
<programlisting>2 months 5 days ago</programlisting>
<para>Note that a relative timestamp is also accepted where a timestamp is expected (see above).</para>
+
+ <para>Use the <command>timestamp</command> command of
+ <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
+ validate and normalize timestamps for testing purposes.</para>
</refsect1>
<refsect1>
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
"Forward" and "reverse" unit properties
</title>
- <tgroup cols='2'>
+ <tgroup cols='4'>
<colspec colname='forward' />
<colspec colname='reverse' />
- <colspec colname='notes' />
+ <colspec colname='fuse' />
+ <colspec colname='ruse' />
<thead>
<row>
<entry>"Forward" property</entry>
<entry>"Reverse" property</entry>
- <entry>Where used</entry>
+ <entry namest='fuse' nameend='ruse' valign='middle'>Where used</entry>
</row>
</thead>
<tbody>
<row>
<entry><varname>Before=</varname></entry>
<entry><varname>After=</varname></entry>
- <entry morerows='1' valign='middle'>Both are unit file options</entry>
+ <entry morerows='1' namest='fuse' nameend='ruse' valign='middle'>[Unit] section</entry>
</row>
<row>
<entry><varname>After=</varname></entry>
<row>
<entry><varname>Requires=</varname></entry>
<entry><varname>RequiredBy=</varname></entry>
- <entry>A unit file option; an option in the [Install] section</entry>
+ <entry>[Unit] section</entry>
+ <entry>[Install] section</entry>
</row>
<row>
<entry><varname>Wants=</varname></entry>
<entry><varname>WantedBy=</varname></entry>
- <entry>A unit file option; an option in the [Install] section</entry>
+ <entry>[Unit] section</entry>
+ <entry>[Install] section</entry>
</row>
<row>
<entry><varname>PartOf=</varname></entry>
<entry><varname>ConsistsOf=</varname></entry>
- <entry>A unit file option; an automatic property</entry>
+ <entry>[Unit] section</entry>
+ <entry>an automatic property</entry>
</row>
<row>
<entry><varname>BindsTo=</varname></entry>
<entry><varname>BoundBy=</varname></entry>
- <entry>A unit file option; an automatic property</entry>
+ <entry>[Unit] section</entry>
+ <entry>an automatic property</entry>
</row>
<row>
<entry><varname>Requisite=</varname></entry>
<entry><varname>RequisiteOf=</varname></entry>
- <entry>A unit file option; an automatic property</entry>
+ <entry>[Unit] section</entry>
+ <entry>an automatic property</entry>
</row>
<row>
<entry><varname>Triggers=</varname></entry>
<entry><varname>TriggeredBy=</varname></entry>
- <entry>Automatic properties, see notes below</entry>
+ <entry namest='fuse' nameend='ruse' valign='middle'>Automatic properties, see notes below</entry>
</row>
<row>
<entry><varname>Conflicts=</varname></entry>
<entry><varname>ConflictedBy=</varname></entry>
- <entry>A unit file option; an automatic property</entry>
+ <entry>[Unit] section</entry>
+ <entry>an automatic property</entry>
</row>
<row>
<entry><varname>PropagatesReloadTo=</varname></entry>
<entry><varname>ReloadPropagatedFrom=</varname></entry>
- <entry morerows='1' valign='middle'>Both are unit file options</entry>
+ <entry morerows='1' namest='fuse' nameend='ruse' valign='middle'>[Unit] section</entry>
</row>
<row>
<entry><varname>ReloadPropagatedFrom=</varname></entry>
<row>
<entry><literal>%h</literal></entry>
<entry>User home directory</entry>
- <entry>This is the home directory of the user running the service manager instance. In case of the system manager this resolves to <literal>/root</literal>.</entry>
+ <entry>This is the home directory of the <emphasis>user running the service manager instance</emphasis>. In case of the system manager this resolves to <literal>/root</literal>.
+
+Note that this setting is <emphasis>not</emphasis> influenced by the <varname>User=</varname> setting configurable in the [Service] section of the service unit.</entry>
</row>
<row>
<entry><literal>%H</literal></entry>
<row>
<entry><literal>%u</literal></entry>
<entry>User name</entry>
- <entry>This is the name of the user running the service manager instance. In case of the system manager this resolves to <literal>root</literal>.</entry>
+ <entry>This is the name of the <emphasis>user running the service manager instance</emphasis>. In case of the system manager this resolves to <literal>root</literal>.
+
+Note that this setting is <emphasis>not</emphasis> influenced by the <varname>User=</varname> setting configurable in the [Service] section of the service unit.</entry>
</row>
<row>
<entry><literal>%U</literal></entry>
<entry>User UID</entry>
- <entry>This is the numeric UID of the user running the service manager instance. In case of the system manager this resolves to <literal>0</literal>.</entry>
+ <entry>This is the numeric UID of the <emphasis>user running the service manager instance</emphasis>. In case of the system manager this resolves to <literal>0</literal>.
+
+Note that this setting is <emphasis>not</emphasis> influenced by the <varname>User=</varname> setting configurable in the [Service] section of the service unit.</entry>
</row>
<row>
<entry><literal>%v</literal></entry>
vtable,
&object));
- while (true) {
+ for (;;) {
check(sd_bus_wait(bus, UINT64_MAX));
check(sd_bus_process(bus, NULL));
}
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
#####################################################################
conf.set_quoted('SYSTEM_SYSVINIT_PATH', sysvinit_path)
conf.set_quoted('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
conf.set_quoted('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
-conf.set_quoted('RC_LOCAL_SCRIPT_PATH_STOP', get_option('halt-local'))
conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper())
substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_path)
substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
substs.set('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
-substs.set('RC_LOCAL_SCRIPT_PATH_STOP', get_option('halt-local'))
substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
substs.set('HIGH_RLIMIT_NOFILE', conf.get('HIGH_RLIMIT_NOFILE'))
+substs.set('BUILD_ROOT', meson.current_build_dir())
#####################################################################
endif
if want_libfuzzer
- fuzzing_engine = meson.get_compiler('cpp').find_library('Fuzzer')
+ fuzzing_engine = meson.get_compiler('cpp').find_library('Fuzzer', required : false)
+ if fuzzing_engine.found()
+ add_project_arguments('-fsanitize-coverage=trace-pc-guard,trace-cmp', language : 'c')
+ elif cc.has_argument('-fsanitize=fuzzer-no-link')
+ add_project_arguments('-fsanitize=fuzzer-no-link', language : 'c')
+ else
+ error('Looks like neither libFuzzer nor -fsanitize=fuzzer-no-link is supported')
+ endif
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 = [
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>'))
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')
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')
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()
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',
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
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
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
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')
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()
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')
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')
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')
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()
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')
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()
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()
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()
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')
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')
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()
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()
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()
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()
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')
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')
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')
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')
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
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
'src/mount/mount-tool.c',
include_directories : includes,
link_with : [libshared],
+ dependencies: [libmount],
install_rpath : rootlibexecdir,
install : true)
public_programs += exe
dependencies = tuple[2]
defs = tuple.length() >= 4 ? tuple[3] : []
incs = tuple.length() >= 5 ? tuple[4] : includes
+ link_args = []
- if fuzzer_build
+ if want_ossfuzz or want_fuzzbuzz
dependencies += fuzzing_engine
+ elif want_libfuzzer
+ if fuzzing_engine.found()
+ dependencies += fuzzing_engine
+ else
+ link_args += ['-fsanitize=fuzzer']
+ endif
else
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(
link_with : link_with,
dependencies : dependencies,
c_args : defs,
+ link_args: link_args,
install : false)
endforeach
endif
############################################################
+check_directives_sh = find_program('tools/check-directives.sh')
+
+if want_tests != 'false'
+ test('check-directives',
+ check_directives_sh,
+ args : project_source_root)
+endif
+
+############################################################
+
# Enable tests for all supported sanitizers
foreach tuple : sanitizers
sanitizer = tuple[0]
'bash completions directory: @0@'.format(bashcompletiondir),
'zsh completions directory: @0@'.format(zshcompletiondir),
'extra start script: @0@'.format(get_option('rc-local')),
- 'extra stop script: @0@'.format(get_option('halt-local')),
'debug shell: @0@ @ @1@'.format(get_option('debug-shell'),
get_option('debug-tty')),
'TTY GID: @0@'.format(tty_gid),
description : 'path to telinit')
option('rc-local', type : 'string',
value : '/etc/rc.local')
-option('halt-local', type : 'string',
- value : '/usr/sbin/halt.local')
option('quotaon-path', type : 'string', description : 'path to quotaon')
option('quotacheck-path', type : 'string', description : 'path to quotacheck')
option('rfkill', type : 'boolean',
description : 'support for the rfkill tools')
option('man', type : 'combo', choices : ['auto', 'true', 'false'],
+ value : 'false',
description : 'build and install man pages')
option('html', type : 'combo', choices : ['auto', 'true', 'false'],
value : 'false',
option('compat-gateway-hostname', type : 'boolean', value : 'false',
description : 'allow "gateway" as the symbolic name for default gateway')
option('default-hierarchy', type : 'combo',
- choices : ['legacy', 'hybrid', 'unified'], value : 'hybrid',
+ choices : ['legacy', 'hybrid', 'unified'], value : 'unified',
description : 'default cgroup hierarchy')
option('default-net-naming-scheme', type : 'combo',
choices : ['latest', 'v238', 'v239', 'v240'],
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')
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"
"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"
#: 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"
#: 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
#: 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"
#: 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
"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
"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"
"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
"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
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"
#: 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"
#: 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"
"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)'."
"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)'."
#
# 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"
#: 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
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."
"É 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"
"É 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)”."
# 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"
#: 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"
#: 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"
#: 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."
"Потрібна автентифікація, щоб установити або забрати змінні середовища з "
"керування службами і системою."
-#: 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."
"Потрібна автентифікація, щоб вказати статично налаштовану назву локального "
"вузла, так само й форматовану."
-#: 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 "Імпортувати образ контейнера або віртуальної машини"
#: 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"
#: 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"
#: 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"
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"
#: 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"
#: 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"
#: 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"
"системою клавіші перемикання кришки."
#: 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 ""
-"Потрібна автентифікація, щоб дозволити незареєстрованим користувачам запускати "
-"програми."
+"Потрібна автентифікація, щоб дозволити незареєстрованим користувачам "
+"запÑ\83Ñ\81каÑ\82и пÑ\80огÑ\80ами."
#: src/login/org.freedesktop.login1.policy:127
msgid "Allow non-logged-in users to run programs"
#: src/login/org.freedesktop.login1.policy:128
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr ""
-"Потрібна автентифікація, щоб дозволити незареєстрованим користувачам запускати "
-"програми."
+"Потрібна автентифікація, щоб дозволити незареєстрованим користувачам "
+"запÑ\83Ñ\81каÑ\82и пÑ\80огÑ\80ами."
#: src/login/org.freedesktop.login1.policy:137
msgid "Allow attaching devices to seats"
#: 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"
"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"
"Authentication is required for rebooting the system while an application "
"asked to inhibit it."
msgstr ""
-"Потрібна автентифікація, щоб перезапустити систему, коли програми намагаються "
-"перешкодити цьому."
+"Потрібна автентифікація, щоб перезапустити систему, коли програми "
+"намагаÑ\8eÑ\82Ñ\8cÑ\81Ñ\8f пеÑ\80еÑ\88кодиÑ\82и Ñ\86Ñ\8cомÑ\83."
#: 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."
"Потрібна автентифікація, щоб зупинити систему, коли інші користувачі в ній."
#: 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."
"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"
msgstr "Потрібна автентифікація, щоб заблокувати або розблокувати сеанси."
#: src/login/org.freedesktop.login1.policy:341
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "Ð\94озволиÑ\82и мÑ\96кÑ\80окодÑ\83 визнаÑ\87аÑ\82и, Ñ\87и заванÑ\82ажÑ\83ваÑ\82и Ñ\96нÑ\82еÑ\80Ñ\84ейÑ\81 вÑ\81Ñ\82ановленнÑ\8f"
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "Ð\92Ñ\81Ñ\82ановленнÑ\8f «пÑ\80иÑ\87ини» пеÑ\80езаванÑ\82аженнÑ\8f Ñ\83 Ñ\8fдÑ\80Ñ\96"
#: 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."
"Потрібна автентифікація, щоб дозволити мікрокоду визначати, чи завантажувати "
"інтерфейс встановлення."
-#: 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 "Потрібна автентифікація, щоб вказати повідомлення на стіні"
#: 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"
"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"
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"
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 ""
-"Потрібна автентифікація, щоб контролювати, чи синхронізування часу через мережу "
-"запущено."
+"Потрібна автентифікація, щоб контролювати, чи синхронізування часу через "
+"меÑ\80ежÑ\83 запÑ\83Ñ\89ено."
-#: 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)»."
+
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-11-22 16:37+0100\n"
-"PO-Revision-Date: 2016-02-12 11:46+0800\n"
-"Last-Translator: Jeff Huang <s8321414@chakraos.org>\n"
+"POT-Creation-Date: 2019-05-24 23:59+0800\n"
+"PO-Revision-Date: 2019-05-25 00:38+0800\n"
+"Last-Translator: pan93412 <pan93412@gmail.com>\n"
"Language-Team: chinese-l10n <chinese-l10n@googlegroups.com>\n"
"Language: zh_TW\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: Lokalize 2.0\n"
+"X-Generator: Poedit 2.2.1\n"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
-msgstr "å\82³å\9b\9eå¯\86碼ç\89\87èª\9eå\88°系統"
+msgstr "å°\87å¯\86碼ç\89\87èª\9eå\82³å\9b\9e系統"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
+#: src/core/org.freedesktop.systemd1.policy.in:23
msgid ""
"Authentication is required to send the entered passphrase back to the system."
-msgstr "å\82³å\9b\9e已輸å\85¥ç\9a\84å¯\86碼ç\89\87èª\9eå\88°系統需要驗證。"
+msgstr "å°\87已輸å\85¥ç\9a\84å¯\86碼ç\89\87èª\9eå\82³å\9b\9e系統需要驗證。"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
+#: src/core/org.freedesktop.systemd1.policy.in:33
msgid "Manage system services or other units"
msgstr "管理系統服務或其他單位"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
+#: src/core/org.freedesktop.systemd1.policy.in:34
msgid "Authentication is required to manage system services or other units."
msgstr "管理系統服務或其他單位需要驗證。"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
+#: src/core/org.freedesktop.systemd1.policy.in:43
msgid "Manage system service or unit files"
msgstr "管理系統服務或單位檔案"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
+#: src/core/org.freedesktop.systemd1.policy.in:44
msgid "Authentication is required to manage system service or unit files."
msgstr "管理系統服務或單位檔案需要驗證。"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
+#: src/core/org.freedesktop.systemd1.policy.in:54
msgid "Set or unset system and service manager environment variables"
-msgstr "設定或取消設定系統及服務管理員環境變量"
+msgstr "設定或取消設定系統及服務管理員環境變數"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
+#: src/core/org.freedesktop.systemd1.policy.in:55
msgid ""
"Authentication is required to set or unset system and service manager "
"environment variables."
-msgstr "設定或取消設定系統及服務管理員環境變量時需要驗證"
+msgstr "需要身份驗證,才能設定或取消設定系統及服務管理員的環境變數。"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "重新載入 systemd 狀態"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10
+#: src/core/org.freedesktop.systemd1.policy.in:65
msgid "Authentication is required to reload the systemd state."
msgstr "重新載入 systemd 狀態需要驗證。"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "設定主機名稱"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
+#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local host name."
msgstr "設定主機名稱需要驗證。"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "設定靜態主機名稱"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
+#: 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."
-msgstr "設定靜態設定的本機主機名稱時需要驗證,同時也需要漂亮的主機名稱"
+msgstr "需要身份驗證,才能設定靜態預先設定或 pretty 本地主機名稱。"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "設定機器資訊"
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6
+#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
-msgstr "設定機器資訊需要驗證"
+msgstr "需要身份驗證,才能設定本地機器資訊。"
-#: ../src/import/org.freedesktop.import1.policy.in.h:1
+#: 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 "匯入虛擬機器或容器映像"
-#: ../src/import/org.freedesktop.import1.policy.in.h:2
+#: 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.in.h:3
+#: src/import/org.freedesktop.import1.policy:32
msgid "Export a VM or container image"
msgstr "匯出虛擬機器或容器映像"
-#: ../src/import/org.freedesktop.import1.policy.in.h:4
+#: src/import/org.freedesktop.import1.policy:33
msgid "Authentication is required to export a VM or container image"
msgstr "匯出虛擬機器或容器映像需要驗證"
-#: ../src/import/org.freedesktop.import1.policy.in.h:5
+#: src/import/org.freedesktop.import1.policy:42
msgid "Download a VM or container image"
msgstr "下載虛擬機器或容器映像"
-#: ../src/import/org.freedesktop.import1.policy.in.h:6
+#: 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.in.h:1
+#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
msgstr "設定系統語系"
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:2
+#: src/locale/org.freedesktop.locale1.policy:23
msgid "Authentication is required to set the system locale."
msgstr "設定系統語系需要驗證。"
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
msgstr "設定系統鍵盤設定"
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:4
+#: src/locale/org.freedesktop.locale1.policy:34
msgid "Authentication is required to set the system keyboard settings."
msgstr "設定系統鍵盤設定需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:22
msgid "Allow applications to inhibit system shutdown"
msgstr "允許應用程式阻止系統關機"
-#: ../src/login/org.freedesktop.login1.policy.in.h:2
+#: src/login/org.freedesktop.login1.policy:23
msgid ""
"Authentication is required for an application to inhibit system shutdown."
msgstr "要讓應用程式阻止系統關機需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:3
+#: src/login/org.freedesktop.login1.policy:33
msgid "Allow applications to delay system shutdown"
msgstr "允許應用程式延遲系統關機"
-#: ../src/login/org.freedesktop.login1.policy.in.h:4
+#: 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.in.h:5
+#: src/login/org.freedesktop.login1.policy:44
msgid "Allow applications to inhibit system sleep"
msgstr "允許應用程式阻止系統睡眠"
-#: ../src/login/org.freedesktop.login1.policy.in.h:6
+#: 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.in.h:7
+#: src/login/org.freedesktop.login1.policy:55
msgid "Allow applications to delay system sleep"
msgstr "允許應用程式延遲系統睡眠"
-#: ../src/login/org.freedesktop.login1.policy.in.h:8
+#: 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.in.h:9
+#: src/login/org.freedesktop.login1.policy:65
msgid "Allow applications to inhibit automatic system suspend"
msgstr "允許應用程式阻止自動系統暫停"
-#: ../src/login/org.freedesktop.login1.policy.in.h:10
+#: src/login/org.freedesktop.login1.policy:66
msgid ""
"Authentication is required for an application to inhibit automatic system "
"suspend."
msgstr "要讓應用程式阻止自動系統暫停需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:11
+#: src/login/org.freedesktop.login1.policy:75
msgid "Allow applications to inhibit system handling of the power key"
msgstr "允許應用程式阻止系統處理電源鍵"
-#: ../src/login/org.freedesktop.login1.policy.in.h:12
+#: src/login/org.freedesktop.login1.policy:76
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the power key."
msgstr "要讓應用程式阻止系統處理電源鍵需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:13
+#: src/login/org.freedesktop.login1.policy:86
msgid "Allow applications to inhibit system handling of the suspend key"
msgstr "允許應用程式阻止系統處理暫停鍵"
-#: ../src/login/org.freedesktop.login1.policy.in.h:14
+#: src/login/org.freedesktop.login1.policy:87
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the suspend key."
msgstr "要讓應用程式阻止系統處理暫停鍵需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:15
+#: src/login/org.freedesktop.login1.policy:97
msgid "Allow applications to inhibit system handling of the hibernate key"
msgstr "允許應用程式阻止系統處理冬眠鍵"
-#: ../src/login/org.freedesktop.login1.policy.in.h:16
+#: src/login/org.freedesktop.login1.policy:98
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the hibernate key."
msgstr "要讓應用程式阻止系統處理冬眠鍵需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:17
+#: src/login/org.freedesktop.login1.policy:107
msgid "Allow applications to inhibit system handling of the lid switch"
msgstr "允許應用程式阻止系統處理上蓋開關"
-#: ../src/login/org.freedesktop.login1.policy.in.h:18
+#: src/login/org.freedesktop.login1.policy:108
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the lid switch."
msgstr "要讓應用程式阻止系統處理上蓋開關需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:19
+#: src/login/org.freedesktop.login1.policy:117
+msgid "Allow non-logged-in user to run programs"
+msgstr "允許未登入的使用者執行程式"
+
+#: src/login/org.freedesktop.login1.policy:118
+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"
msgstr "允許未登入的使用者執行程式"
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: 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.in.h:21
+#: src/login/org.freedesktop.login1.policy:137
msgid "Allow attaching devices to seats"
msgstr "允許將設備連接到座位"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
+#: src/login/org.freedesktop.login1.policy:138
msgid "Authentication is required for attaching a device to a seat."
msgstr "將設備連接到座位需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: src/login/org.freedesktop.login1.policy:148
msgid "Flush device to seat attachments"
msgstr "暴露裝置以安裝附件"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
+#: src/login/org.freedesktop.login1.policy:149
msgid ""
"Authentication is required for resetting how devices are attached to seats."
msgstr "要重置裝置如何連接到座位需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: src/login/org.freedesktop.login1.policy:158
msgid "Power off the system"
msgstr "關閉系統電源"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
+#: src/login/org.freedesktop.login1.policy:159
msgid "Authentication is required for powering off the system."
-msgstr "關閉系統電源需要驗證"
+msgstr "關閉系統電源需要身份驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: src/login/org.freedesktop.login1.policy:169
msgid "Power off the system while other users are logged in"
msgstr "在有其他使用者登入時關閉系統電源"
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: src/login/org.freedesktop.login1.policy:170
msgid ""
"Authentication is required for powering off the system while other users are "
"logged in."
msgstr "在有其他使用者登入時關閉系統電源需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
+#: src/login/org.freedesktop.login1.policy:180
msgid "Power off the system while an application asked to inhibit it"
msgstr "當應用程式阻止系統電源關閉時將其關閉"
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
+#: src/login/org.freedesktop.login1.policy:181
msgid ""
"Authentication is required for powering off the system while an application "
"asked to inhibit it."
msgstr "當應用程式阻止系統電源關閉時將系統電源關閉需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: src/login/org.freedesktop.login1.policy:191
msgid "Reboot the system"
msgstr "重新啟動系統"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
+#: src/login/org.freedesktop.login1.policy:192
msgid "Authentication is required for rebooting the system."
msgstr "重新啟動系統需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: src/login/org.freedesktop.login1.policy:202
msgid "Reboot the system while other users are logged in"
msgstr "在有其他使用者登入時重新啟動系統"
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: src/login/org.freedesktop.login1.policy:203
msgid ""
"Authentication is required for rebooting the system while other users are "
"logged in."
msgstr "在有其他使用者登入時重新啟動系統需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
+#: src/login/org.freedesktop.login1.policy:213
msgid "Reboot the system while an application asked to inhibit it"
msgstr "當應用程式阻止重新啟動系統時將系統重新啟動"
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
+#: src/login/org.freedesktop.login1.policy:214
msgid ""
"Authentication is required for rebooting the system while an application "
"asked to inhibit it."
msgstr "當應用程式阻止系統重新啟動時將系統重新啟動需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Halt the system"
+msgstr "停止系統"
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid "Authentication is required for halting the system."
+msgstr "停止系統需要身份驗證。"
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system while other users are logged in"
+msgstr "在其他使用者登入時停止系統"
+
+#: src/login/org.freedesktop.login1.policy:236
+msgid ""
+"Authentication is required for halting the system while other users are "
+"logged in."
+msgstr "在其他使用者登入時停止系統需要身份驗證。"
+
+#: src/login/org.freedesktop.login1.policy:246
+msgid "Halt the system while an application asked to inhibit it"
+msgstr "在應用程式阻止時停止系統"
+
+#: src/login/org.freedesktop.login1.policy:247
+msgid ""
+"Authentication is required for halting the system while an application asked "
+"to inhibit it."
+msgstr "需要身份驗證,才能在應用程式阻止時停止系統。"
+
+#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "暫停系統"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
+#: src/login/org.freedesktop.login1.policy:258
msgid "Authentication is required for suspending the system."
msgstr "暫停系統需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: src/login/org.freedesktop.login1.policy:267
msgid "Suspend the system while other users are logged in"
msgstr "在有其他使用者登入時暫停系統"
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: src/login/org.freedesktop.login1.policy:268
msgid ""
"Authentication is required for suspending the system while other users are "
"logged in."
msgstr "在有其他使用者登入時暫停系統需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
+#: src/login/org.freedesktop.login1.policy:278
msgid "Suspend the system while an application asked to inhibit it"
msgstr "當應用程式阻止暫停系統時將系統暫停"
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
+#: src/login/org.freedesktop.login1.policy:279
msgid ""
"Authentication is required for suspending the system while an application "
"asked to inhibit it."
msgstr "當應用程式阻止系統暫停時將系統暫停需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: src/login/org.freedesktop.login1.policy:289
msgid "Hibernate the system"
msgstr "系統冬眠"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
+#: src/login/org.freedesktop.login1.policy:290
msgid "Authentication is required for hibernating the system."
msgstr "系統冬眠需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: src/login/org.freedesktop.login1.policy:299
msgid "Hibernate the system while other users are logged in"
msgstr "在有其他使用者登入時冬眠系統"
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: src/login/org.freedesktop.login1.policy:300
msgid ""
"Authentication is required for hibernating the system while other users are "
"logged in."
msgstr "在有其他使用者登入時冬眠系統需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
+#: src/login/org.freedesktop.login1.policy:310
msgid "Hibernate the system while an application asked to inhibit it"
msgstr "當應用程式阻止冬眠系統時將系統冬眠"
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
+#: src/login/org.freedesktop.login1.policy:311
msgid ""
"Authentication is required for hibernating the system while an application "
"asked to inhibit it."
msgstr "當應用程式阻止系統冬眠時將系統冬眠需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: src/login/org.freedesktop.login1.policy:321
msgid "Manage active sessions, users and seats"
msgstr "管理活躍的工作階段、使用者與座位"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
+#: src/login/org.freedesktop.login1.policy:322
msgid ""
"Authentication is required for managing active sessions, users and seats."
msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: src/login/org.freedesktop.login1.policy:331
msgid "Lock or unlock active sessions"
msgstr "鎖定或解鎖活躍的工作階段"
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: src/login/org.freedesktop.login1.policy:332
msgid "Authentication is required to lock or unlock active sessions."
msgstr "鎖定或解鎖活躍的工作階段需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "允許對韌體的指示以開始設定介面"
+#: src/login/org.freedesktop.login1.policy:341
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "設定內核中的重新啟動 \"reason (原因)\""
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr "需要身份驗證,才能設定內核中的重新啟動 \"reason (原因)\"。"
+
+#: 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."
msgstr "對韌體的指示以開始設定介面需要驗證。"
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: 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 "設定 wall 訊息"
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: src/login/org.freedesktop.login1.policy:386
msgid "Authentication is required to set a wall message"
-msgstr "設定 wall 訊息需要驗證。"
+msgstr "需要身份驗證,才能設定 wall 訊息"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "登入到本機容器"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:2
+#: src/machine/org.freedesktop.machine1.policy:23
msgid "Authentication is required to log into a local container."
msgstr "登入到本機容器需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
msgstr "登入到本機主機"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: src/machine/org.freedesktop.machine1.policy:33
msgid "Authentication is required to log into the local host."
msgstr "登入到本機主機需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: src/machine/org.freedesktop.machine1.policy:42
msgid "Acquire a shell in a local container"
msgstr "在本機容器中取得一個 shell"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: src/machine/org.freedesktop.machine1.policy:43
msgid "Authentication is required to acquire a shell in a local container."
msgstr "在本機容器中取得一個 shell 需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+#: src/machine/org.freedesktop.machine1.policy:53
msgid "Acquire a shell on the local host"
msgstr "在本機主機中取得一個 shell"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#: src/machine/org.freedesktop.machine1.policy:54
msgid "Authentication is required to acquire a shell on the local host."
msgstr "在本機主機中取得一個 shell 需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#: src/machine/org.freedesktop.machine1.policy:64
msgid "Acquire a pseudo TTY in a local container"
msgstr "取得在本機容器中的偽 TTY"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#: src/machine/org.freedesktop.machine1.policy:65
msgid ""
"Authentication is required to acquire a pseudo TTY in a local container."
msgstr "取得在本機容器中的偽 TTY 需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+#: src/machine/org.freedesktop.machine1.policy:74
msgid "Acquire a pseudo TTY on the local host"
msgstr "取得在本機主機中的偽 TTY"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#: src/machine/org.freedesktop.machine1.policy:75
msgid "Authentication is required to acquire a pseudo TTY on the local host."
msgstr "取得在本機主機中的偽 TTY 需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+#: src/machine/org.freedesktop.machine1.policy:84
msgid "Manage local virtual machines and containers"
msgstr "管理本機虛擬機器及容器"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
+#: src/machine/org.freedesktop.machine1.policy:85
msgid ""
"Authentication is required to manage local virtual machines and containers."
msgstr "管理本機虛擬機器及容器需要驗證。"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
+#: src/machine/org.freedesktop.machine1.policy:95
msgid "Manage local virtual machine and container images"
msgstr "管理本機虛擬機器及容器映像"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
+#: src/machine/org.freedesktop.machine1.policy:96
msgid ""
"Authentication is required to manage local virtual machine and container "
"images."
msgstr "管理本機虛擬機器及容器映像需要驗證。"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+#: 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 register a DNS-SD service"
+msgstr "需要身份驗證,才能註冊 DNS-SD 服務"
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr "取消註冊 DNS-SD 服務"
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr "需要身份驗證,才能取消註冊 DNS-SD 服務"
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "設定系統時間"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2
+#: src/timedate/org.freedesktop.timedate1.policy:23
msgid "Authentication is required to set the system time."
msgstr "設定系統時間需要驗證。"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+#: src/timedate/org.freedesktop.timedate1.policy:33
msgid "Set system timezone"
msgstr "設定系統時區"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4
+#: src/timedate/org.freedesktop.timedate1.policy:34
msgid "Authentication is required to set the system timezone."
msgstr "設定系統時區需要驗證。"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5
+#: src/timedate/org.freedesktop.timedate1.policy:43
msgid "Set RTC to local timezone or UTC"
msgstr "將 RTC 設定為本地時區或 UTC"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6
+#: src/timedate/org.freedesktop.timedate1.policy:44
msgid ""
"Authentication is required to control whether the RTC stores the local or "
"UTC time."
msgstr "控制 RTC 儲存本地或 UTC 時間需要驗證。"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7
+#: src/timedate/org.freedesktop.timedate1.policy:53
msgid "Turn network time synchronization on or off"
msgstr "打開或關閉網路時間同步"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8
+#: 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:428
+#: src/core/dbus-unit.c:317
msgid "Authentication is required to start '$(unit)'."
msgstr "啟動 '$(unit)' 需要驗證。"
-#: ../src/core/dbus-unit.c:429
+#: src/core/dbus-unit.c:318
msgid "Authentication is required to stop '$(unit)'."
msgstr "停止 '$(unit)' 需要驗證。"
-#: ../src/core/dbus-unit.c:430
+#: src/core/dbus-unit.c:319
msgid "Authentication is required to reload '$(unit)'."
msgstr "重新載入 '$(unit)' 需要驗證。"
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#: src/core/dbus-unit.c:320 src/core/dbus-unit.c:321
msgid "Authentication is required to restart '$(unit)'."
msgstr "重新啟動 '$(unit)' 需要驗證。"
-#: ../src/core/dbus-unit.c:535
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "砍除 '$(unit)' 需要驗證。"
+#: src/core/dbus-unit.c:493
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr "需要身份驗證,才能傳送 UNIX 信號至「$(unit)」的程序。"
-#: ../src/core/dbus-unit.c:565
+#: src/core/dbus-unit.c:524
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr "重置 '$(unit)' 的「失敗」狀態需要驗證。"
-#: ../src/core/dbus-unit.c:597
+#: src/core/dbus-unit.c:557
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "在 '$(unit)' 上設定屬性需要驗證。"
+#~ msgid "Authentication is required to kill '$(unit)'."
+#~ msgstr "砍除 '$(unit)' 需要驗證。"
SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name"
SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k"
-SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target"
-ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target"
-SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target"
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
+ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target"
+SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target"
-SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
-SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
+SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
SUBSYSTEM=="udc", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="usb-gadget.target"
# default to Debian testing
DISTRO=${DISTRO:-debian}
RELEASE=${RELEASE:-buster}
+BRANCH=${BRANCH:-experimental}
ARCH=${ARCH:-amd64}
CONTAINER=${RELEASE}-${ARCH}
MAX_CACHE_AGE=604800 # one week
;;
RUN)
# add current debian/ packaging
- git fetch --depth=1 https://salsa.debian.org/systemd-team/systemd.git master
+ git fetch --depth=1 https://salsa.debian.org/systemd-team/systemd.git $BRANCH
git checkout FETCH_HEAD debian
# craft changelog
# disable autopkgtests which are not for upstream
sed -i '/# NOUPSTREAM/ q' debian/tests/control
# enable more unit tests
- sed -i '/^CONFFLAGS =/ s/=/= -Dtests=unsafe -Dsplit-usr=true -Dslow-tests=true /' debian/rules
+ sed -i '/^CONFFLAGS =/ s/=/= -Dtests=unsafe -Dsplit-usr=true -Dslow-tests=true -Dman=true /' debian/rules
# no orig tarball
echo '1.0' > debian/source/format
local i verb comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local -A OPTS=(
- [STANDALONE]='-h --help --no-variables -p --print-path --version --no-pager'
- [ARG]='--path'
+ [STANDALONE]='-h --help --no-variables -p --print-esp-path -x --print-boot-path --version --no-pager'
+ [ARG]='--esp-path --boot-path'
)
if __contains_word "$prev" ${OPTS[ARG]}; then
case $prev in
- --path)
+ --esp-path|--boot-path)
if [[ -z $cur ]]; then
comps=$(compgen -A directory -- "/" )
else
_arguments \
{-h,--help}'[Show this help]' \
'--version[Show package version]' \
- '--what=[Operations to inhibit]:options:_inhibit_what' \
+ '--what=[Operations to inhibit]:options:_systemd-inhibit_what' \
'--who=[A descriptive string who is inhibiting]:who is inhibiting:' \
'--why=[A descriptive string why is being inhibited]:reason for the lock:' \
'--mode=[One of block or delay]:lock mode:( block delay )' \
int r, n;
int epoll_fd = -1;
+ log_show_color(true);
log_parse_environment();
log_open();
}
#endif
+static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
+ if (calendar && calendar_spec_from_string(p, NULL) >= 0)
+ log_notice("Hint: this expression is a valid calendar specification. "
+ "Use 'systemd-analyze calendar \"%s\"' instead?", p);
+ if (timestamp && parse_timestamp(p, NULL) >= 0)
+ log_notice("Hint: this expression is a valid timestamp. "
+ "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
+ if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
+ log_notice("Hint: this expression is a valid timespan. "
+ "Use 'systemd-analyze timespan \"%s\"' instead?", p);
+}
+
static int dump_timespan(int argc, char *argv[], void *userdata) {
char **input_timespan;
char ft_buf[FORMAT_TIMESPAN_MAX];
r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
- if (r < 0)
- return log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
+ parsing_hint(*input_timespan, true, true, false);
+ return r;
+ }
printf("Original: %s\n", *input_timespan);
printf(" %ss: %" PRIu64 "\n", special_glyph(SPECIAL_GLYPH_MU), output_usecs);
- printf(" Human: %s\n", format_timespan(ft_buf, sizeof(ft_buf), output_usecs, usec_magnitude));
+ printf(" Human: %s%s%s\n",
+ ansi_highlight(),
+ format_timespan(ft_buf, sizeof(ft_buf), output_usecs, usec_magnitude),
+ ansi_normal());
if (input_timespan[1])
putchar('\n');
return EXIT_SUCCESS;
}
+static int test_timestamp_one(const char *p) {
+ usec_t usec;
+ char buf[FORMAT_TIMESTAMP_MAX];
+ int r;
+
+ r = parse_timestamp(p, &usec);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse \"%s\": %m", p);
+ parsing_hint(p, true, false, true);
+ return r;
+ }
+
+ printf(" Original form: %s\n", p);
+ printf("Normalized form: %s%s%s\n",
+ ansi_highlight_blue(),
+ format_timestamp(buf, sizeof buf, usec),
+ ansi_normal());
+
+ if (!in_utc_timezone())
+ printf(" (in UTC): %s\n", format_timestamp_utc(buf, sizeof buf, usec));
+
+ printf(" UNIX seconds: @%"PRI_USEC"%s%0*"PRI_USEC"\n",
+ usec / USEC_PER_SEC,
+ usec % USEC_PER_SEC ? "." : "",
+ usec % USEC_PER_SEC ? 6 : 0,
+ usec % USEC_PER_SEC);
+
+ printf(" From now: %s\n", format_timestamp_relative(buf, sizeof buf, usec));
+
+ return 0;
+}
+
+static int test_timestamp(int argc, char *argv[], void *userdata) {
+ int ret = 0, r;
+ char **p;
+
+ STRV_FOREACH(p, strv_skip(argv, 1)) {
+ r = test_timestamp_one(*p);
+ if (ret == 0 && r < 0)
+ ret = r;
+
+ if (*(p + 1))
+ putchar('\n');
+ }
+
+ return ret;
+}
+
static int test_calendar_one(usec_t n, const char *p) {
_cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
_cleanup_free_ char *t = NULL;
int r;
r = calendar_spec_from_string(p, &spec);
- if (r < 0)
- return log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
-
- r = calendar_spec_normalize(spec);
- if (r < 0)
- return log_error_errno(r, "Failed to normalize calendar specification '%s': %m", p);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
+ parsing_hint(p, false, true, true);
+ return r;
+ }
r = calendar_spec_to_string(spec, &t);
if (r < 0)
" --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"
" unit-paths List load directories for units\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
" verify FILE... Check unit files for correctness\n"
- " calendar SPEC... Validate repetitive calendar time events\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
+ " calendar SPEC... Validate repetitive calendar time events\n"
+ " timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
"\nSee the %s for details.\n"
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
{ "verify", 2, VERB_ANY, 0, do_verify },
{ "calendar", 2, VERB_ANY, 0, test_calendar },
- { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
+ { "timestamp", 2, VERB_ANY, 0, test_timestamp },
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
+ { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
{}
};
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();
char **p;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
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;
}
if (r < 0)
return r;
- assert_cc(sizeof(hi) == sizeof(unsigned));
- assert_cc(sizeof(lo) == sizeof(unsigned));
-
- k = sscanf(p, "%u %u", &lo, &hi);
+ k = sscanf(p, "%" PRIu32 " %" PRIu32, &lo, &hi);
if (k != 2)
return -EIO;
- current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
+ current = (uint64_t) lo | ((uint64_t) hi << 32);
after = current & keep;
if (current == after)
return 0;
- lo = (unsigned) (after & 0xFFFFFFFFULL);
- hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
+ lo = after & UINT32_C(0xFFFFFFFF);
+ hi = (after >> 32) & UINT32_C(0xFFFFFFFF);
- return write_string_filef(fn, WRITE_STRING_FILE_CREATE, "%u %u", lo, hi);
+ return write_string_filef(fn, 0, "%" PRIu32 " %" PRIu32, lo, hi);
}
int capability_bounding_set_drop_usermode(uint64_t keep) {
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
- if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
+ if (strlen(e) > sc_arg_max() - 3)
return false;
return true;
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
- if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
+ if (strlen(e) > sc_arg_max() - 1)
return false;
return true;
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
+#include <unistd.h>
#include "macro.h"
#include "string.h"
+static inline size_t sc_arg_max(void) {
+ long l = sysconf(_SC_ARG_MAX);
+ assert(l > 0);
+ return (size_t) l;
+}
+
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
return cunescape_length(s, strlen(s), flags, ret);
}
-char *xescape(const char *s, const char *bad) {
- char *r, *t;
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
+ char *ans, *t, *prev, *prev2;
const char *f;
- /* Escapes all chars in bad, in addition to \ and all special
- * chars, in \xFF style escaping. May be reversed with
- * cunescape(). */
+ /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
+ * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
+ * This corresponds to non-ASCII printable characters in pre-unicode encodings.
+ *
+ * If console_width is reached, output is truncated and "..." is appended. */
- r = new(char, strlen(s) * 4 + 1);
- if (!r)
+ if (console_width == 0)
+ return strdup("");
+
+ ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
+ if (!ans)
return NULL;
- for (f = s, t = r; *f; f++) {
+ memset(ans, '_', MIN(strlen(s), console_width) * 4);
+ ans[MIN(strlen(s), console_width) * 4] = 0;
+
+ for (f = s, t = prev = prev2 = ans; ; f++) {
+ char *tmp_t = t;
+
+ if (!*f) {
+ *t = 0;
+ return ans;
+ }
+
+ if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
+ *f == '\\' || strchr(bad, *f)) {
+ if ((size_t) (t - ans) + 4 > console_width)
+ break;
- if ((*f < ' ') || (*f >= 127) ||
- (*f == '\\') || strchr(bad, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
- } else
+ } else {
+ if ((size_t) (t - ans) + 1 > console_width)
+ break;
+
*(t++) = *f;
+ }
+
+ /* We might need to go back two cycles to fit three dots, so remember two positions */
+ prev2 = prev;
+ prev = tmp_t;
}
- *t = 0;
+ /* We can just write where we want, since chars are one-byte */
+ size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
+ size_t off;
+ if (console_width - c >= (size_t) (t - ans))
+ off = (size_t) (t - ans);
+ else if (console_width - c >= (size_t) (prev - ans))
+ off = (size_t) (prev - ans);
+ else if (console_width - c >= (size_t) (prev2 - ans))
+ off = (size_t) (prev2 - ans);
+ else
+ off = console_width - c;
+ assert(off <= (size_t) (t - ans));
- return r;
+ memcpy(ans + off, "...", c);
+ ans[off + c] = '\0';
+ return ans;
+}
+
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
+ if (eight_bit)
+ return xescape_full(str, "", console_width, true);
+ else
+ return utf8_escape_non_printable_full(str, console_width);
}
char *octescape(const char *s, size_t len) {
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
-char *xescape(const char *s, const char *bad);
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
+static inline char *xescape(const char *s, const char *bad) {
+ return xescape_full(s, bad, SIZE_MAX, false);
+}
char *octescape(const char *s, size_t len);
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
char *shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, EscapeStyle style);
#include <stdbool.h>
#include <stdint.h>
#include <sys/inotify.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
}
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+
+static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
+ assert(addr);
+
+ siphash24_compress(addr, sizeof(*addr), state);
+}
+
+static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in6_addr_hash_ops;
-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
__u64 tree_bytes_scrubbed; /* # of tree bytes scrubbed */
__u64 read_errors; /* # of read errors encountered (EIO) */
__u64 csum_errors; /* # of failed csum checks */
- __u64 verify_errors; /* # of occurrences, where the metadata
+ __u64 verify_errors; /* # of occurences, where the metadata
* of a tree block did not match the
* expected values, like generation or
* logical */
__u64 last_physical; /* last physical address scrubbed. In
* case a scrub was aborted, this can
* be used to restart the scrub */
- __u64 unverified_errors; /* # of occurrences where a read for a
+ __u64 unverified_errors; /* # of occurences where a read for a
* full (64k) bio failed, but the re-
* check succeeded for each 4k piece.
* Intermittent error. */
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
/* for storing balance parameters in the root tree */
#define BTRFS_BALANCE_OBJECTID -4ULL
-/* orphan objectid for tracking unlinked/truncated files */
+/* orhpan objectid for tracking unlinked/truncated files */
#define BTRFS_ORPHAN_OBJECTID -5ULL
/* does write ahead logging to speed up fsyncs */
#define BTRFS_PERSISTENT_ITEM_KEY 249
/*
- * Persistently stores the device replace state in the device tree.
+ * Persistantly stores the device replace state in the device tree.
* The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
*/
#define BTRFS_DEV_REPLACE_KEY 250
*
* 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
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,
};
};
/*
- * 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)
*
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;
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
-
+
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
__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 */
/*
#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 ] */
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)
#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
#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.. */
#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
#define IPV6_PMTUDISC_WANT 1
#define IPV6_PMTUDISC_DO 2
#define IPV6_PMTUDISC_PROBE 3
-/* same as IPV6_PMTUDISC_PROBE, provided for symmetry with IPv4
+/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4
* also see comments on IP_PMTUDISC_INTERFACE
*/
#define IPV6_PMTUDISC_INTERFACE 4
--- /dev/null
+#!/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
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)
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) {
_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")
return STR_IN_SET(fstype,
"adfs",
+ "exfat",
"fat",
"hfs",
"hpfs",
}
if (detect_container() > 0)
- return get_process_cmdline(1, 0, false, ret);
+ return get_process_cmdline(1, SIZE_MAX, 0, ret);
else
return read_one_line_file("/proc/cmdline", ret);
}
#include "alloc-util.h"
#include "architecture.h"
#include "escape.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "ioprio.h"
+#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "string-util.h"
#include "terminal-util.h"
#include "user-util.h"
+#include "utf8.h"
-int get_process_state(pid_t pid) {
+/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own
+ * workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel.
+ */
+#define COMM_MAX_LEN 128
+
+static int get_process_state(pid_t pid) {
const char *p;
char state;
int r;
assert(ret);
assert(pid >= 0);
- escaped = new(char, TASK_COMM_LEN);
+ escaped = new(char, COMM_MAX_LEN);
if (!escaped)
return -ENOMEM;
return r;
/* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
- cellescape(escaped, TASK_COMM_LEN, comm);
+ cellescape(escaped, COMM_MAX_LEN, comm);
*ret = TAKE_PTR(escaped);
return 0;
}
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_fclose_ FILE *f = NULL;
- bool space = false;
- char *k;
- _cleanup_free_ char *ans = NULL;
+ _cleanup_free_ char *t = NULL, *ans = NULL;
const char *p;
- int c, r;
+ int r;
+ size_t k;
+
+ /* This is supposed to be a safety guard against runaway command lines. */
+ size_t max_length = sc_arg_max();
assert(line);
assert(pid >= 0);
- /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
- * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
- * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
- * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
- * command line that resolves to the empty string will return the "comm" name of the process instead.
+ /* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (�). If
+ * max_columns is != -1 will return a string of the specified console width at most, abbreviated with
+ * an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
+ * line set (the case for kernel threads), or has a command line that resolves to the empty string
+ * will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
+ * input data.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* comm_fallback is false). Returns 0 and sets *line otherwise. */
if (r < 0)
return r;
- if (max_length == 0) {
- /* This is supposed to be a safety guard against runaway command lines. */
- long l = sysconf(_SC_ARG_MAX);
- assert(l > 0);
- max_length = l;
- }
-
- if (max_length == 1) {
+ /* We assume that each four-byte character uses one or two columns. If we ever check for combining
+ * characters, this assumption will need to be adjusted. */
+ if ((size_t) 4 * max_columns + 1 < max_columns)
+ max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
- /* If there's only room for one byte, return the empty string */
- ans = new0(char, 1);
- if (!ans)
- return -ENOMEM;
+ t = new(char, max_length);
+ if (!t)
+ return -ENOMEM;
- *line = TAKE_PTR(ans);
- return 0;
+ k = fread(t, 1, max_length, f);
+ if (k > 0) {
+ /* Arguments are separated by NULs. Let's replace those with spaces. */
+ for (size_t i = 0; i < k - 1; i++)
+ if (t[i] == '\0')
+ t[i] = ' ';
+ t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
} else {
- bool dotdotdot = false;
- size_t left;
-
- ans = new(char, max_length);
- if (!ans)
- return -ENOMEM;
-
- k = ans;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
-
- if (space) {
- if (left <= 2) {
- dotdotdot = true;
- break;
- }
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 1) {
- dotdotdot = true;
- break;
- }
-
- *(k++) = (char) c;
- left--;
- } else if (k > ans)
- space = true;
- }
-
- if (dotdotdot) {
- if (max_length <= 4) {
- k = ans;
- left = max_length;
- } else {
- k = ans + max_length - 4;
- left = 4;
-
- /* Eat up final spaces */
- while (k > ans && isspace(k[-1])) {
- k--;
- left++;
- }
- }
-
- strncpy(k, "...", left-1);
- k[left-1] = 0;
- } else
- *k = 0;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(ans)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- ans = mfree(ans);
+ /* We only treat getting nothing as an error. We *could* also get an error after reading some
+ * data, but we ignore that case, as such an error is rather unlikely and we prefer to get
+ * some data rather than none. */
+ if (ferror(f))
+ return -errno;
- if (!comm_fallback)
+ if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- size_t l = strlen(t);
-
- if (l + 3 <= max_length) {
- ans = strjoin("[", t, "]");
- if (!ans)
- return -ENOMEM;
-
- } else if (max_length <= 6) {
- ans = new(char, max_length);
- if (!ans)
- return -ENOMEM;
+ /* Kernel threads have no argv[] */
+ _cleanup_free_ char *t2 = NULL;
- memcpy(ans, "[...]", max_length-1);
- ans[max_length-1] = 0;
- } else {
- t[max_length - 6] = 0;
+ r = get_process_comm(pid, &t2);
+ if (r < 0)
+ return r;
- /* Chop off final spaces */
- delete_trailing_chars(t, WHITESPACE);
+ mfree(t);
+ t = strjoin("[", t2, "]");
+ if (!t)
+ return -ENOMEM;
+ }
- ans = strjoin("[", t, "...]");
- if (!ans)
- return -ENOMEM;
- }
+ delete_trailing_chars(t, WHITESPACE);
- *line = TAKE_PTR(ans);
- return 0;
- }
+ bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
- k = realloc(ans, strlen(ans) + 1);
- if (!k)
+ ans = escape_non_printable_full(t, max_columns, eight_bit);
+ if (!ans)
return -ENOMEM;
- ans = NULL;
- *line = k;
-
+ (void) str_realloc(&ans);
+ *line = TAKE_PTR(ans);
return 0;
}
* can use PR_SET_NAME, which sets the thread name for the calling thread. */
if (prctl(PR_SET_NAME, name) < 0)
log_debug_errno(errno, "PR_SET_NAME failed: %m");
- if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */
+ if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
truncated = true;
/* Second step, change glibc's ID of the process name. */
_r_; \
})
-int get_process_state(pid_t pid);
+typedef enum ProcessCmdlineFlags {
+ PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
+ PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
+} ProcessCmdlineFlags;
+
int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
#include "io-util.h"
#include "missing.h"
#include "random-util.h"
+#include "siphash24.h"
#include "time-util.h"
int rdrand(unsigned long *ret) {
+ /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here,
+ * instead of sticking to /dev/urandom or getrandom()?
+ *
+ * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and
+ * getrandom() is generally not initialized yet. It is very common that initialization of the random
+ * pool takes a longer time (up to many minutes), in particular on embedded devices that have no
+ * explicit hardware random generator, as well as in virtualized environments such as major cloud
+ * installations that do not provide virtio-rng or a similar mechanism.
+ *
+ * In such an environment using getrandom() synchronously means we'd block the entire system boot-up
+ * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK)
+ * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean
+ * generating many kmsg log messages about our use of it before the random pool is properly
+ * initialized. Neither of these outcomes is desirable.
+ *
+ * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND
+ * provides us quickly and relatively reliably with random values, without having to delay boot,
+ * without triggering warning messages in kmsg.
+ *
+ * Note that we use RDRAND only under very specific circumstances, when the requirements on the
+ * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use
+ * RDRAND:
+ *
+ * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic
+ * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be
+ * generated in a way that is reliably unique, but they do not require ultimate trust into
+ * the entropy generator. systemd generates a number of UUIDs during early boot, including
+ * 'invocation IDs' for every unit spawned that identify the specific invocation of the
+ * service globally, and a number of others. Other alternatives for generating these UUIDs
+ * have been considered, but don't really work: for example, hashing uuids from a local
+ * system identifier combined with a counter falls flat because during early boot disk
+ * storage is not yet available (think: initrd) and thus a system-specific ID cannot be
+ * stored or retrieved yet.
+ *
+ * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are
+ * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive
+ * O(n) access complexity if an attacker manages to trigger a large number of hash
+ * collisions. Thus, systemd (as any software employing hash tables should) uses seeded
+ * hash functions for its hash tables, with a seed generated randomly. The hash tables
+ * systemd employs watch the fill level closely and reseed if necessary. This allows use of
+ * a low quality RNG initially, as long as it improves should a hash table be under attack:
+ * the attacker after all needs to to trigger many collisions to exploit it for the purpose
+ * of DoS, but if doing so improves the seed the attack surface is reduced as the attack
+ * takes place.
+ *
+ * Some cases where we do NOT use RDRAND are:
+ *
+ * • Generation of cryptographic key material 🔑
+ *
+ * • Generation of cryptographic salt values 🧂
+ *
+ * This function returns:
+ *
+ * -EOPNOTSUPP → RDRAND is not available on this system 😔
+ * -EAGAIN → The operation failed this time, but is likely to work if you try again a few
+ * times ♻
+ * -EUCLEAN → We got some random value, but it looked strange, so we refused using it.
+ * This failure might or might not be temporary. 😕
+ */
+
#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;
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)
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;
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 high-quality randomness from the kernel (or potentially mid-quality randomness from
+ * 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 RANDOM_EXTEND_WITH_PSEUDO is set, and some but not
+ * enough better quality randomness could be acquired, the rest is filled up with low quality
+ * randomness.
+ *
+ * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND
+ * or even RANDOM_EXTEND_WITH_PSEUDO.
+ *
+ * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use
+ * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via
+ * an "all bets are off" wrapper, such as random_bytes(), see below. */
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;
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)) {
return 0;
}
- if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
+ if (FLAGS_SET(flags, RANDOM_MAY_FAIL))
return -ENODATA;
/* Use /dev/urandom instead */
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;
void pseudo_random_bytes(void *p, size_t n) {
uint8_t *q;
+ /* This returns pseudo-random data using libc's rand() function. You probably never want to call this
+ * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes()
+ * instead, see below: it will fall back to this function if there's nothing better to get, but only
+ * then. */
+
initialize_srand();
for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
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)
+ /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason
+ * it is not available we'll try some crappy fallbacks.
+ *
+ * What this function will do:
+ *
+ * • This function will preferably use the CPU's RDRAND operation, if it is available, in
+ * order to return "mid-quality" random values cheaply.
+ *
+ * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are
+ * cheaply available.
+ *
+ * • This function will return pseudo-random data, generated via libc rand() if nothing
+ * better is available.
+ *
+ * • This function will work fine in early boot
+ *
+ * • This function will always succeed
+ *
+ * What this function won't do:
+ *
+ * • This function will never fail: it will give you randomness no matter what. It might not
+ * be high quality, but it will return some, possibly generated via libc's rand() call.
+ *
+ * • This function will never block: if the only way to get good randomness is a blocking,
+ * synchronous getrandom() we'll instead provide you with pseudo-random data.
+ *
+ * This function is hence great for things like seeding hash tables, generating random numeric UNIX
+ * user IDs (that are checked for collisions before use) and such.
+ *
+ * This function is hence not useful for generating UUIDs or cryptographic key material.
+ */
+
+ 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. */
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. */
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)) {
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)
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) {
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;
#include <netinet/in.h>
#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
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;
return (uint8_t*) p + n;
}
+
+static inline char* str_realloc(char **p) {
+ /* Reallocate *p to actual size */
+
+ if (!*p)
+ return NULL;
+
+ char *t = realloc(*p, strlen(*p) + 1);
+ if (!t)
+ return NULL;
+
+ return (*p = t);
+}
return parse_boolean(b);
}
+int vt_verify_kbmode(int fd) {
+ int curr_mode;
+
+ /*
+ * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
+ * Otherwise we would (likely) interfere with X11's processing of the
+ * key events.
+ *
+ * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
+ */
+
+ if (ioctl(fd, KDGKBMODE, &curr_mode) < 0)
+ return -errno;
+
+ return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY;
+}
+
int vt_reset_keyboard(int fd) {
- int kb;
+ int kb, r;
/* If we can't read the default, then default to unicode. It's 2017 after all. */
kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
+ r = vt_verify_kbmode(fd);
+ if (r == -EBUSY) {
+ log_debug_errno(r, "Keyboard is not in XLATE or UNICODE mode, not resetting: %m");
+ return 0;
+ } else if (r < 0)
+ return r;
+
if (ioctl(fd, KDSKBMODE, kb) < 0)
return -errno;
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;
+ }
+}
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
+#include <syslog.h>
#include <sys/types.h>
#include "macro.h"
#include "time-util.h"
/* Regular colors */
-#define ANSI_BLACK "\x1B[0;30m"
#define ANSI_RED "\x1B[0;31m"
#define ANSI_GREEN "\x1B[0;32m"
#define ANSI_YELLOW "\x1B[0;33m"
#define ANSI_BLUE "\x1B[0;34m"
#define ANSI_MAGENTA "\x1B[0;35m"
-#define ANSI_CYAN "\x1B[0;36m"
-#define ANSI_WHITE "\x1B[0;37m"
-#define ANSI_GREY "\x1B[0;2;37m"
+#define ANSI_GREY "\x1B[0;38;5;245m"
/* Bold/highlighted */
-#define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m"
#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m"
-#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m"
+#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;38;5;185m"
#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
#define ANSI_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
-#define ANSI_HIGHLIGHT_CYAN "\x1B[0;1;36m"
-#define ANSI_HIGHLIGHT_WHITE "\x1B[0;1;37m"
+#define ANSI_HIGHLIGHT_GREY "\x1B[0;1;38;5;245m"
/* Underlined */
-#define ANSI_HIGHLIGHT_BLACK_UNDERLINE "\x1B[0;1;4;30m"
#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m"
#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
#define ANSI_HIGHLIGHT_MAGENTA_UNDERLINE "\x1B[0;1;4;35m"
-#define ANSI_HIGHLIGHT_CYAN_UNDERLINE "\x1B[0;1;4;36m"
-#define ANSI_HIGHLIGHT_WHITE_UNDERLINE "\x1B[0;1;4;37m"
+#define ANSI_HIGHLIGHT_GREY_UNDERLINE "\x1B[0;1;4;38;5;245m"
/* Other ANSI codes */
#define ANSI_UNDERLINE "\x1B[0;4m"
colors_enabled() ? ANSI_##REPLACEMENT : ""; \
}
-DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
-DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
-DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
-DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
-DEFINE_ANSI_FUNC(normal, NORMAL);
-DEFINE_ANSI_FUNC(grey, GREY);
-
-DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(normal, NORMAL);
+DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
+DEFINE_ANSI_FUNC(red, RED);
+DEFINE_ANSI_FUNC(green, GREEN);
+DEFINE_ANSI_FUNC(yellow, YELLOW);
+DEFINE_ANSI_FUNC(blue, BLUE);
+DEFINE_ANSI_FUNC(magenta, MAGENTA);
+DEFINE_ANSI_FUNC(grey, GREY);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
+DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY);
+
+DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA_UNDERLINE, HIGHLIGHT_MAGENTA);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_grey_underline, HIGHLIGHT_GREY_UNDERLINE, HIGHLIGHT_GREY);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);
int open_terminal_in_namespace(pid_t pid, const char *name, int mode);
int vt_default_utf8(void);
+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);
*/
assert(t);
- assert(usec);
if (t[0] == '@' && !with_tz)
return parse_sec(t + 1, usec);
else
return -EINVAL;
- *usec = ret;
-
+ if (usec)
+ *usec = ret;
return 0;
}
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
- if (tmp.return_value == 0)
+ if (tmp.return_value == 0 && usec)
*usec = tmp.usec;
return tmp.return_value;
bool something = false;
assert(t);
- assert(usec);
assert(default_unit > 0);
p = t;
if (*s != 0)
return -EINVAL;
- *usec = USEC_INFINITY;
+ if (usec)
+ *usec = USEC_INFINITY;
return 0;
}
}
}
- *usec = r;
-
+ if (usec)
+ *usec = r;
return 0;
}
#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)
#include "gunicode.h"
#include "hexdecoct.h"
#include "macro.h"
+#include "string-util.h"
#include "utf8.h"
bool unichar_is_valid(char32_t ch) {
}
*s = '\0';
-
+ (void) str_realloc(&p);
return p;
}
-char *utf8_escape_non_printable(const char *str) {
- char *p, *s;
+static int utf8_char_console_width(const char *str) {
+ char32_t c;
+ int r;
+
+ r = utf8_encoded_to_unichar(str, &c);
+ if (r < 0)
+ return r;
+
+ /* TODO: we should detect combining characters */
+
+ return unichar_iswide(c) ? 2 : 1;
+}
+
+char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
+ char *p, *s, *prev_s;
+ size_t n = 0; /* estimated print width */
assert(str);
- p = s = malloc(strlen(str) * 4 + 1);
+ if (console_width == 0)
+ return strdup("");
+
+ p = s = prev_s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
- while (*str) {
+ for (;;) {
int len;
+ char *saved_s = s;
+
+ if (!*str) /* done! */
+ goto finish;
len = utf8_encoded_valid_unichar(str, (size_t) -1);
if (len > 0) {
if (utf8_is_printable(str, len)) {
+ int w;
+
+ w = utf8_char_console_width(str);
+ assert(w >= 0);
+ if (n + w > console_width)
+ goto truncation;
+
s = mempcpy(s, str, len);
str += len;
+ n += w;
+
} else {
- while (len > 0) {
+ for (; len > 0; len--) {
+ if (n + 4 > console_width)
+ goto truncation;
+
*(s++) = '\\';
*(s++) = 'x';
*(s++) = hexchar((int) *str >> 4);
*(s++) = hexchar((int) *str);
str += 1;
- len--;
+ n += 4;
}
}
} else {
- s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+ if (n + 1 > console_width)
+ goto truncation;
+
+ s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
str += 1;
+ n += 1;
}
+
+ prev_s = saved_s;
}
- *s = '\0';
+ truncation:
+ /* Try to go back one if we don't have enough space for the ellipsis */
+ if (n + 1 >= console_width)
+ s = prev_s;
+
+ s = mempcpy(s, "…", strlen("…"));
+ finish:
+ *s = '\0';
+ (void) str_realloc(&p);
return p;
}
/* Returns the approximate width a string will take on screen when printed on a character cell
* terminal/console. */
- while (*str != 0) {
- char32_t c;
+ while (*str) {
+ int w;
- if (utf8_encoded_to_unichar(str, &c) < 0)
+ w = utf8_char_console_width(str);
+ if (w < 0)
return (size_t) -1;
+ n += w;
str = utf8_next_char(str);
-
- n += unichar_iswide(c) ? 2 : 1;
}
return n;
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
-char *utf8_escape_non_printable(const char *str);
+char *utf8_escape_non_printable_full(const char *str, size_t console_width);
+static inline char *utf8_escape_non_printable(const char *str) {
+ return utf8_escape_non_printable_full(str, (size_t) -1);
+}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
size_t utf16_encode_unichar(char16_t *out, char32_t c);
static void boot_entry_file_list(const char *field, const char *root, const char *p, int *ret_status) {
int status = boot_entry_file_check(root, p);
- printf("%13s%s", strempty(field), field ? ":" : " ");
+ printf("%13s%s ", strempty(field), field ? ":" : " ");
if (status < 0) {
errno = -status;
printf("%s%s%s (%m)\n", ansi_highlight_red(), p, ansi_normal());
" --esp-path=PATH Path to the EFI System Partition (ESP)\n"
" --boot-path=PATH Path to the $BOOT partition\n"
" -p --print-esp-path Print path to the EFI System Partition\n"
- " --print-boot-path Print path to the $BOOT partition\n"
+ " -x --print-boot-path Print path to the $BOOT partition\n"
" --no-variables Don't touch EFI variables\n"
" --no-pager Do not pipe output into a pager\n"
"\nBoot Loader Commands:\n"
enum {
ARG_ESP_PATH = 0x100,
ARG_BOOT_PATH,
- ARG_PRINT_BOOT_PATH,
ARG_VERSION,
ARG_NO_VARIABLES,
ARG_NO_PAGER,
{ "boot-path", required_argument, NULL, ARG_BOOT_PATH },
{ "print-esp-path", no_argument, NULL, 'p' },
{ "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
- { "print-boot-path", no_argument, NULL, ARG_PRINT_BOOT_PATH },
+ { "print-boot-path", no_argument, NULL, 'x' },
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hpx", options, NULL)) >= 0)
switch (c) {
case 'h':
break;
case 'p':
+ if (arg_print_dollar_boot_path)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
arg_print_esp_path = true;
break;
- case ARG_PRINT_BOOT_PATH:
+ case 'x':
+ if (arg_print_esp_path)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
arg_print_dollar_boot_path = true;
break;
static int run(int argc, char *argv[]) {
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
static int run(int argc, char *argv[]) {
int r, output_flags;
+ log_show_color(true);
log_parse_environment();
log_open();
CGroupMask mask;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root);
if (r < 0)
return log_error_errno(r, "Failed to get root control group path: %m");
- log_debug("Cgroup path: %s", root);
+ log_debug("CGroup path: %s", root);
a = hashmap_new(&group_hash_ops);
b = hashmap_new(&group_hash_ops);
log_debug_errno(r, "Failed to enable cgroup empty event source: %m");
}
-static int unit_check_oom(Unit *u) {
+int unit_check_oom(Unit *u) {
_cleanup_free_ char *oom_kill = NULL;
bool increased;
uint64_t c;
int unit_watch_cgroup_memory(Unit *u);
void unit_add_to_cgroup_empty_queue(Unit *u);
+int unit_check_oom(Unit *u);
int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path);
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),
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
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(u, name, &c->default_memory_min, message, flags, error);
+ 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);
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_scale(u, name, &c->default_memory_min, message, flags, error);
+ 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);
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;
p = buf;
}
- (void) get_process_cmdline(pid, 0, true, &cmdline);
+ (void) get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &cmdline);
return sd_bus_message_append(reply,
"(sus)",
if (skip_seccomp_unavailable(u, "SystemCallFilter="))
return 0;
- negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
+ negative_action = c->syscall_errno == 0 ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno);
if (c->syscall_whitelist) {
default_action = negative_action;
const ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
- const ExecRuntime *runtime) {
+ const ExecRuntime *runtime,
+ char **error_path) {
_cleanup_strv_free_ char **empty_directories = NULL;
char *tmp = NULL, *var = NULL;
needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
context->mount_flags,
- DISSECT_IMAGE_DISCARD_ON_LOOP);
+ DISSECT_IMAGE_DISCARD_ON_LOOP,
+ error_path);
bind_mount_free_many(bind_mounts, n_bind_mounts);
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
- r = apply_mount_namespace(unit, command, context, params, runtime);
+ _cleanup_free_ char *error_path = NULL;
+
+ r = apply_mount_namespace(unit, command, context, params, runtime, &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
- return log_unit_error_errno(unit, r, "Failed to set up mount namespacing: %m");
+ return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
+ error_path ? ": " : "", strempty(error_path));
}
}
$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)
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;
}
if (setenv("TERM", t, 1) < 0)
return -errno;
+ /* The kernels sets HOME=/ for init. Let's undo this. */
+ if (path_equal_ptr(getenv("HOME"), "/") &&
+ unsetenv("HOME") < 0)
+ log_warning_errno(errno, "Failed to unset $HOME: %m");
+
return 0;
}
assert(source);
assert(m);
- /* First we call waitd() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it
+ /* First we call waitid() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it
* while it is a zombie. */
if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
/* Finally, execute them all. Note that u1, u2 and the array might contain duplicates, but
* that's fine, manager_invoke_sigchld_event() will ensure we only invoke the handlers once for
* each iteration. */
- if (u1)
+ if (u1) {
+ /* We check for oom condition, in case we got SIGCHLD before the oom notification.
+ * We only do this for the cgroup the PID belonged to. */
+ (void) unit_check_oom(u1);
+
manager_invoke_sigchld_event(m, u1, &si);
+ }
if (u2)
manager_invoke_sigchld_event(m, u2, &si);
if (array_copy)
meson.add_install_script('sh', '-c', mkdir_p.format(systemgeneratordir))
meson.add_install_script('sh', '-c', mkdir_p.format(usergeneratordir))
-meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(pkgsysconfdir, 'system/multi-user.target.wants')))
-meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(pkgsysconfdir, 'system/getty.target.wants')))
meson.add_install_script('sh', '-c',
mkdir_p.format(join_paths(pkgsysconfdir, 'user')))
meson.add_install_script('sh', '-c',
}
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);
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
- DissectImageFlags dissect_image_flags) {
+ DissectImageFlags dissect_image_flags,
+ char **error_path) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
if (!proc_self_mountinfo) {
r = log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
+ if (error_path)
+ *error_path = strdup("/proc/self/mountinfo");
goto finish;
}
continue;
r = follow_symlink(root, m);
- if (r < 0)
+ if (r < 0) {
+ if (error_path && mount_entry_path(m))
+ *error_path = strdup(mount_entry_path(m));
goto finish;
+ }
if (r == 0) {
/* We hit a symlinked mount point. The entry got rewritten and might point to a
* very different place now. Let's normalize the changed list, and start from
}
r = apply_mount(root, m);
- if (r < 0)
+ if (r < 0) {
+ if (error_path && mount_entry_path(m))
+ *error_path = strdup(mount_entry_path(m));
goto finish;
+ }
m->applied = true;
}
/* Second round, flip the ro bits if necessary. */
for (m = mounts; m < mounts + n_mounts; ++m) {
r = make_read_only(m, blacklist, proc_self_mountinfo);
- if (r < 0)
+ if (r < 0) {
+ if (error_path && mount_entry_path(m))
+ *error_path = strdup(mount_entry_path(m));
goto finish;
+ }
}
}
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
- DissectImageFlags dissected_image_flags);
+ DissectImageFlags dissected_image_flags,
+ char **error_path);
int setup_tmp_dirs(
const char *id,
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);
* aren't as rigoriously written to protect aganst against multiple use. */
if (unit_warn_leftover_processes(UNIT(s)) &&
IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) &&
- !s->kill_context.send_sigkill) {
- return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY), "Will not start SendSIGKILL=no service of type KillMode=control-group or mixed while processes exist");
- }
+ !s->kill_context.send_sigkill)
+ return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY),
+ "Will not start SendSIGKILL=no service of type KillMode=control-group or mixed while processes exist");
+
return 0;
}
case TIMER_UNIT_ACTIVE:
leave_around = true;
- base = trigger->inactive_exit_timestamp.monotonic;
-
- if (base <= 0)
- base = t->last_trigger.monotonic;
-
+ base = MAX(trigger->inactive_exit_timestamp.monotonic, t->last_trigger.monotonic);
if (base <= 0)
continue;
- base = MAX(base, t->last_trigger.monotonic);
-
break;
case TIMER_UNIT_INACTIVE:
leave_around = true;
- base = trigger->inactive_enter_timestamp.monotonic;
-
- if (base <= 0)
- base = t->last_trigger.monotonic;
-
+ base = MAX(trigger->inactive_enter_timestamp.monotonic, t->last_trigger.monotonic);
if (base <= 0)
continue;
- base = MAX(base, t->last_trigger.monotonic);
-
break;
default:
if (r < 0)
return r;
- r = get_process_cmdline(container_pid, 0, false, cmdline);
+ r = get_process_cmdline(container_pid, SIZE_MAX, 0, cmdline);
if (r < 0)
return r;
* (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;
if (sd_pid_get_slice(pid, &t) >= 0)
set_iovec_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t);
- if (get_process_cmdline(pid, 0, false, &t) >= 0)
+ if (get_process_cmdline(pid, SIZE_MAX, 0, &t) >= 0)
set_iovec_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
static int run_debug(int argc, char **argv, void *userdata) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
- _cleanup_free_ char *exe = NULL, *path = NULL;
+ _cleanup_free_ char *exe = NULL, *path = NULL, *debugger = NULL;
bool unlink_path = false;
const char *data, *fork_name;
size_t len;
arg_debugger = "gdb";
}
+ debugger = strdup(arg_debugger);
+ if (!debugger)
+ return -ENOMEM;
+
if (arg_field) {
log_error("Option --field/-F only makes sense with list");
return -EINVAL;
/* Don't interfere with gdb and its handling of SIGINT. */
(void) ignore_signals(SIGINT, -1);
- fork_name = strjoina("(", arg_debugger, ")");
+ fork_name = strjoina("(", debugger, ")");
r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
goto finish;
if (r == 0) {
- execlp(arg_debugger, arg_debugger, exe, "-c", path, NULL);
+ execlp(debugger, debugger, exe, "-c", path, NULL);
log_open();
- log_error_errno(errno, "Failed to invoke %s: %m", arg_debugger);
+ log_error_errno(errno, "Failed to invoke %s: %m", debugger);
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate_and_check(arg_debugger, pid, WAIT_LOG_ABNORMAL);
+ r = wait_for_terminate_and_check(debugger, pid, WAIT_LOG_ABNORMAL);
finish:
(void) default_signals(SIGINT, -1);
int r, units_active;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
static int run(int argc, char *argv[]) {
int r, k, n_found = 0;
+ log_show_color(true);
log_parse_environment();
log_open();
* to detect whether we are being run in a virtualized
* environment or not */
+ log_show_color(true);
log_parse_environment();
log_open();
char **i;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
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)
--- /dev/null
+/* 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;
+}
/* 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);
--- /dev/null
+#include "fuzz.h"
+
+int FuzzerEntrypoint(const uint8_t *data, size_t size) {
+ return LLVMFuzzerTestOneInput(data, size);
+}
[libshared],
[]],
+ [['src/fuzz/fuzz-varlink.c'],
+ [libshared],
+ []],
+
[['src/fuzz/fuzz-unit-file.c'],
[libcore,
libshared],
[['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],
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
static int run(int argc, char *argv[]) {
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
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));
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);
void *userdata) {
MHDDaemonWrapper *d = userdata;
int r;
- MHD_UNSIGNED_LONG_LONG timeout = ULONG_LONG_MAX;
+ MHD_UNSIGNED_LONG_LONG timeout = ULLONG_MAX;
assert(d);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"MHD_run failed!");
if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
- timeout = ULONG_LONG_MAX;
+ timeout = ULLONG_MAX;
r = sd_event_source_set_time(d->timer_event, timeout);
if (r < 0) {
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);
_cleanup_close_ int outfd = -1, errfd = -1, saved_stderr = -1;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
}
JournalFile* journal_file_close(JournalFile *f) {
- assert(f);
+ if (!f)
+ return NULL;
#if HAVE_GCRYPT
/* Write the final tag */
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);
}
r = mmap_cache_get(f->mmap, f->cache_fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h, NULL);
+ if (r == -EINVAL) {
+ /* Some file systems (jffs2 or p9fs) don't support mmap() properly (or only read-only
+ * mmap()), and return EINVAL in that case. Let's propagate that as a more recognizable error
+ * code. */
+ r = -EAFNOSUPPORT;
+ goto fail;
+ }
if (r < 0)
goto fail;
int journal_file_set_offline(JournalFile *f, bool wait);
bool journal_file_is_offlining(JournalFile *f);
JournalFile* journal_file_close(JournalFile *j);
+DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close);
int journal_file_open_reliably(
const char *fname,
for (i = 0; i < j; i++)
fputs("\xe2\x96\x88", stdout);
- fputs(ANSI_NORMAL, stdout);
+ fputs(ansi_normal(), stdout);
for (i = 0; i < k; i++)
fputs("\xe2\x96\x91", stdout);
#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"
#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
+#include "varlink.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
ACTION_UPDATE_CATALOG,
ACTION_LIST_BOOTS,
ACTION_FLUSH,
+ ACTION_RELINQUISH_VAR,
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
" --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"
ARG_UTC,
ARG_SYNC,
ARG_FLUSH,
+ ARG_RELINQUISH_VAR,
+ ARG_SMART_RELINQUISH_VAR,
ARG_ROTATE,
ARG_VACUUM_SIZE,
ARG_VACUUM_FILES,
};
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 },
{}
};
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;
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));
-
- watch_fd = watch_run_systemd_journal(IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR);
- if (watch_fd < 0)
- return watch_fd;
-
- for (;;) {
- if (access("/run/systemd/journal/flushed", F_OK) >= 0)
- return 0;
+ return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m");
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to check for existence of /run/systemd/journal/flushed: %m");
+ (void) varlink_set_description(link, "journal");
- 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) {
int n_shown = 0, r, poll_fd = -1;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
r = flush_to_var();
goto finish;
+ case ACTION_RELINQUISH_VAR:
+ r = relinquish_var();
+ goto finish;
+
case ACTION_SYNC:
r = sync_journal();
goto finish;
free(arg_verify_key);
#if HAVE_PCRE2
- if (arg_compiled_pattern)
+ if (arg_compiled_pattern) {
pcre2_code_free(arg_compiled_pattern);
+
+ /* --grep was used, no error was thrown, but the pattern didn't
+ * match anything. Let's mimic grep's behavior here and return
+ * a non-zero exit code, so journalctl --grep can be used
+ * in scripts and such */
+ if (r == 0 && n_shown == 0)
+ r = -ENOENT;
+ }
#endif
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
#include "alloc-util.h"
#include "audit-util.h"
#include "cgroup-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
if (r < 0) {
log_warning_errno(r, "Cannot query /proc/meminfo for MemTotal: %m");
cached = CACHE_MAX_FALLBACK;
- } else {
+ } else
/* Cache entries are usually a few kB, but the process cmdline is controlled by the
* user and can be up to _SC_ARG_MAX, usually 2MB. Let's say that approximately up to
* 1/8th of memory may be used by the cache.
*
* In the common case, this formula gives 64 cache entries for each GB of RAM.
*/
- long l = sysconf(_SC_ARG_MAX);
- assert(l > 0);
-
- cached = CLAMP(mem_total / 8 / (uint64_t) l, CACHE_MAX_MIN, CACHE_MAX_MAX);
- }
+ cached = CLAMP(mem_total / 8 / sc_arg_max(), CACHE_MAX_MIN, CACHE_MAX_MAX);
}
return cached;
if (get_process_exe(c->pid, &t) >= 0)
free_and_replace(c->exe, t);
- if (get_process_cmdline(c->pid, 0, false, &t) >= 0)
+ if (get_process_cmdline(c->pid, SIZE_MAX, 0, &t) >= 0)
free_and_replace(c->cmdline, t);
if (get_process_capeff(c->pid, &t) >= 0)
JournalMetrics *metrics,
JournalFile **ret) {
- JournalFile *f;
+ _cleanup_(journal_file_closep) JournalFile *f = NULL;
int r;
assert(s);
return r;
r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC);
- if (r < 0) {
- (void) journal_file_close(f);
+ if (r < 0)
return r;
- }
- *ret = f;
+ *ret = TAKE_PTR(f);
return r;
}
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.
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
* 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
log_warning("%s: Journal file is from the future, rotating.", f->path);
return true;
+ case -EAFNOSUPPORT:
+ log_warning("%s: underlying file system does not support memory mapping or another required file system feature.", f->path);
+ return false;
+
default:
return false;
}
char ts[FORMAT_TIMESPAN_MAX];
usec_t start;
unsigned n = 0;
- int r;
+ int r, k;
assert(s);
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;
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;
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);
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;
}
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. */
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;
}
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;
return r;
}
+ r = server_open_varlink(s);
+ if (r < 0)
+ return r;
+
r = server_open_kernel_seqnum(s);
if (r < 0)
return r;
(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) {
client_context_flush_all(s);
- if (s->system_journal)
- (void) journal_file_close(s->system_journal);
-
- if (s->runtime_journal)
- (void) journal_file_close(s->runtime_journal);
+ (void) journal_file_close(s->system_journal);
+ (void) journal_file_close(s->runtime_journal);
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);
#include "list.h"
#include "prioq.h"
#include "time-util.h"
+#include "varlink.h"
typedef enum Storage {
STORAGE_AUTO,
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="))
assert(data);
hex_buf = hexmem(data, size);
- if (hex_buf == NULL)
+ if (!hex_buf)
return -ENOMEM;
fprintf(f, "%s=%s\n", key, hex_buf);
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;
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)
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) {
(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;
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;
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);
*/
/* 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,
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;
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;
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;
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)
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,
DHCP_CLIENT_DONT_DESTROY(client);
client->state = DHCP_STATE_RENEWING;
- client->attempt = 1;
+ client->attempt = 0;
return client_initialize_time_events(client);
}
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;
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(),
notify_event = r;
client->state = DHCP_STATE_BOUND;
- client->attempt = 1;
+ client->attempt = 0;
client->last_addr = client->lease->address;
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);
.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) {
LIST_REMOVE(prefix, ra->prefixes, cur);
ra->n_prefixes--;
+ sd_radv_prefix_unref(cur);
break;
}
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;
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);
printf(" DHCP address acquired\n");
sd_event_exit(e, 0);
+
+ return 0;
}
static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
LIBSYSTEMD_243 {
global:
sd_bus_object_vtable_format;
+ sd_event_source_disable_unref;
} LIBSYSTEMD_241;
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)
if (r < 0)
return r;
- if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature));
+ if (type != 'v')
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_SIGNATURE,
+ "Incorrect signature when setting property '%s', expected 'v', got '%c'.",
+ c->member, type);
+ if (!streq(strempty(signature), strempty(c->vtable->x.property.signature)))
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS,
+ "Incorrect parameters for property '%s', expected '%s', got '%s'.",
+ c->member, strempty(c->vtable->x.property.signature), strempty(signature));
r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
if (r < 0)
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);
[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[] = {
[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[] = {
[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",
.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),
/*
[IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
*/
+ [IFLA_MIN_MTU] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MAX_MTU] = { .type = NETLINK_TYPE_U32 },
};
static const NLTypeSystem rtnl_link_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 = {
[RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
[RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_METRICS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
-/* [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
-*/
+ [RTA_MULTIPATH] = { .size = sizeof(struct rtnexthop) },
[RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */
-/*
- RTA_CACHEINFO,
- RTA_TABLE,
- RTA_MARK,
- RTA_MFC_STATS,
- RTA_VIA,
- RTA_NEWDST,
-*/
+ [RTA_CACHEINFO] = { .size = sizeof(struct rta_cacheinfo) },
+ [RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MARK] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
+ [RTA_VIA] = { .type = NETLINK_TYPE_U32 },
+ [RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
[RTA_PREF] = { .type = NETLINK_TYPE_U8 },
-/*
- RTA_ENCAP_TYPE,
- RTA_ENCAP,
- */
[RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
+ [RTA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [RTA_ENCAP] = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
+ [RTA_UID] = { .type = NETLINK_TYPE_U32 },
+ [RTA_TTL_PROPAGATE] = { .type = NETLINK_TYPE_U8 },
+ [RTA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [RTA_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [RTA_DPORT] = { .type = NETLINK_TYPE_U16 },
};
static const NLTypeSystem rtnl_route_type_system = {
[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 = {
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,
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
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");
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
return 0;
}
-static int button_suitable(Button *b) {
+static int button_suitable(int fd) {
unsigned long types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1];
- assert(b);
- assert(b->fd);
+ assert(fd >= 0);
- if (ioctl(b->fd, EVIOCGBIT(EV_SYN, sizeof(types)), types) < 0)
+ if (ioctl(fd, EVIOCGBIT(EV_SYN, sizeof types), types) < 0)
return -errno;
if (bitset_get(types, EV_KEY)) {
unsigned long keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1];
- if (ioctl(b->fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys) < 0)
+ if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keys), keys) < 0)
return -errno;
if (bitset_get(keys, KEY_POWER) ||
if (bitset_get(types, EV_SW)) {
unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1];
- if (ioctl(b->fd, EVIOCGBIT(EV_SW, sizeof(switches)), switches) < 0)
+ if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof switches), switches) < 0)
return -errno;
if (bitset_get(switches, SW_LID) ||
return false;
}
-static int button_set_mask(Button *b) {
+static int button_set_mask(const char *name, int fd) {
unsigned long
types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1] = {},
keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1] = {},
switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
struct input_mask mask;
- assert(b);
- assert(b->fd >= 0);
+ assert(name);
+ assert(fd >= 0);
bitset_put(types, EV_KEY);
bitset_put(types, EV_SW);
.codes_ptr = PTR_TO_UINT64(types),
};
- if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
+ if (ioctl(fd, EVIOCSMASK, &mask) < 0)
/* Log only at debug level if the kernel doesn't do EVIOCSMASK yet */
return log_full_errno(IN_SET(errno, ENOTTY, EOPNOTSUPP, EINVAL) ? LOG_DEBUG : LOG_WARNING,
- errno, "Failed to set EV_SYN event mask on /dev/input/%s: %m", b->name);
+ errno, "Failed to set EV_SYN event mask on /dev/input/%s: %m", name);
bitset_put(keys, KEY_POWER);
bitset_put(keys, KEY_POWER2);
.codes_ptr = PTR_TO_UINT64(keys),
};
- if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
- return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", b->name);
+ if (ioctl(fd, EVIOCSMASK, &mask) < 0)
+ return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", name);
bitset_put(switches, SW_LID);
bitset_put(switches, SW_DOCK);
.codes_ptr = PTR_TO_UINT64(switches),
};
- if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
- return log_warning_errno(errno, "Failed to set EV_SW event mask on /dev/input/%s: %m", b->name);
+ if (ioctl(fd, EVIOCSMASK, &mask) < 0)
+ return log_warning_errno(errno, "Failed to set EV_SW event mask on /dev/input/%s: %m", name);
return 0;
}
int button_open(Button *b) {
- char *p, name[256];
+ _cleanup_close_ int fd = -1;
+ const char *p;
+ char name[256];
int r;
assert(b);
p = strjoina("/dev/input/", b->name);
- b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (b->fd < 0)
+ fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (fd < 0)
return log_warning_errno(errno, "Failed to open %s: %m", p);
- r = button_suitable(b);
+ r = button_suitable(fd);
if (r < 0)
- return log_warning_errno(r, "Failed to determine whether input device is relevant to us: %m");
+ return log_warning_errno(r, "Failed to determine whether input device %s is relevant to us: %m", p);
if (r == 0)
return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL),
- "Device %s does not expose keys or switches relevant to us, ignoring.",
- p);
-
- if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
- r = log_error_errno(errno, "Failed to get input name: %m");
- goto fail;
- }
+ "Device %s does not expose keys or switches relevant to us, ignoring.", p);
- (void) button_set_mask(b);
+ if (ioctl(fd, EVIOCGNAME(sizeof name), name) < 0)
+ return log_error_errno(errno, "Failed to get input name for %s: %m", p);
- r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
- if (r < 0) {
- log_error_errno(r, "Failed to add button event: %m");
- goto fail;
- }
-
- log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
+ (void) button_set_mask(b->name, fd);
+
+ b->io_event_source = sd_event_source_unref(b->io_event_source);
+ r = sd_event_add_io(b->manager->event, &b->io_event_source, fd, EPOLLIN, button_dispatch, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add button event for %s: %m", p);
+ b->fd = TAKE_FD(fd);
+ log_info("Watching system buttons on %s (%s)", p, name);
return 0;
-
-fail:
- b->fd = safe_close(b->fd);
- return r;
}
int button_check_switches(Button *b) {
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;
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) {
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");
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
#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"
}
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);
/* 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;
}
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
networkd-address.h
networkd-brvlan.c
networkd-brvlan.h
+ networkd-can.c
+ networkd-can.h
networkd-conf.c
networkd-conf.h
networkd-dhcp4.c
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
#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"
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,
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <netinet/in.h>
#include <linux/if_bonding.h>
#include "in-addr-util.h"
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_;
#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"
+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;
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;
/* 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;
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);
/* 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"
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m");
+ if (IN_SET(t->peer_family, AF_INET, AF_INET6)) {
+ r = sd_netlink_message_append_u16(m, FOU_ATTR_PEER_PORT, htobe16(t->peer_port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_PORT attribute: %m");
+ }
+
switch (t->fou_encap_type) {
case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT:
encap_type = FOU_ENCAP_DIRECT;
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 if (t->local_family == AF_INET6) {
+ 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 if (t->peer_family == AF_INET6){
+ 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;
}
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;
assert_not_reached("Invalid fou encap type");
}
+ if (t->peer_family == AF_UNSPEC && t->peer_port > 0)
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "FooOverUDP peer port is set but peer address not configured in %s. Rejecting configuration.",
+ filename);
+ else if (t->peer_family != AF_UNSPEC && t->peer_port == 0)
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "FooOverUDP peer port not set but peer address is configured in %s. Rejecting configuration.",
+ filename);
return 0;
}
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <netinet/in.h>
#include <linux/fou.h>
#include "in-addr-util.h"
uint8_t fou_protocol;
uint16_t port;
+ uint16_t peer_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);
CONFIG_PARSER_PROTOTYPE(config_parse_fou_encap_type);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol);
+CONFIG_PARSER_PROTOTYPE(config_parse_fou_tunnel_address);
#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"
#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;
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");
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");
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);
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;
}
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;
#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;
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);
#include "conf-parser.h"
#include "netdev/ipvlan.h"
+#include "networkd-link.h"
#include "string-table.h"
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
assert(link);
assert(netdev->ifname);
- m = IPVLAN(netdev);
+ if (netdev->kind == NETDEV_KIND_IPVLAN)
+ m = IPVLAN(netdev);
+ else
+ m = IPVTAP(netdev);
assert(m);
assert(n);
- m = IPVLAN(n);
+ if (n->kind == NETDEV_KIND_IPVLAN)
+ m = IPVLAN(n);
+ else
+ m = IPVTAP(n);
assert(m);
.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,
+};
+
+IPVlanMode link_get_ipvlan_mode(Link *link) {
+ NetDev *netdev;
+
+ if (!streq_ptr(link->kind, "ipvlan"))
+ return _NETDEV_IPVLAN_MODE_INVALID;
+
+ if (netdev_get(link->manager, link->ifname, &netdev) < 0)
+ return _NETDEV_IPVLAN_MODE_INVALID;
+
+ if (netdev->kind != NETDEV_KIND_IPVLAN)
+ return _NETDEV_IPVLAN_MODE_INVALID;
+
+ return IPVLAN(netdev)->mode;
+}
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <netinet/in.h>
#include <linux/if_link.h>
#include "netdev/netdev.h"
} 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_;
CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags);
+
+IPVlanMode link_get_ipvlan_mode(Link *link);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <arpa/inet.h>
+#include <netinet/in.h>
#include <linux/l2tp.h>
#include <linux/genetlink.h>
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <netinet/in.h>
#include <linux/l2tp.h>
#include "in-addr-util.h"
/* 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>
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <netinet/in.h>
#include <linux/if_macsec.h>
#include "in-addr-util.h"
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)
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.PeerPort, config_parse_ip_port, 0, offsetof(FouTunnel, peer_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)
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)
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)
MACsecReceiveAssociation.Key, config_parse_macsec_key, 0, 0
MACsecReceiveAssociation.KeyFile, config_parse_macsec_key_file, 0, 0
MACsecReceiveAssociation.Activate, config_parse_macsec_sa_activate, 0, 0
-Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tun.OneQueue, config_parse_warn_compat, DISABLED_LEGACY, 0
Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tun.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tap.OneQueue, config_parse_warn_compat, DISABLED_LEGACY, 0
Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
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
[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,
[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",
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");
}
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,
NETDEV_KIND_MACVLAN,
NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN,
+ NETDEV_KIND_IPVTAP,
NETDEV_KIND_VXLAN,
NETDEV_KIND_IPIP,
NETDEV_KIND_GRE,
/* 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>
#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"
if (!t->packet_info)
ifr->ifr_flags |= IFF_NO_PI;
- if (t->one_queue)
- ifr->ifr_flags |= IFF_ONE_QUEUE;
-
if (t->multi_queue)
ifr->ifr_flags |= IFF_MULTI_QUEUE;
assert(netdev);
if (netdev->mtu != 0)
- log_netdev_warning(netdev, "MTU configured for %s, ignoring", netdev_kind_to_string(netdev->kind));
+ log_netdev_warning(netdev,
+ "MTUBytes= configured for %s device in %s will be ignored.\n"
+ "Please set it in the corresponding .network file.",
+ netdev_kind_to_string(netdev->kind), filename);
if (netdev->mac)
- log_netdev_warning(netdev, "MAC configured for %s, ignoring", netdev_kind_to_string(netdev->kind));
+ log_netdev_warning(netdev,
+ "MACAddress= configured for %s device in %s will be ignored.\n"
+ "Please set it in the corresponding .network file.",
+ netdev_kind_to_string(netdev->kind), filename);
return 0;
}
char *user_name;
char *group_name;
- bool one_queue;
bool multi_queue;
bool packet_info;
bool vnet_hdr;
typedef struct VCan VCan;
+#include <netinet/in.h>
#include <linux/can/netlink.h>
#include "netdev/netdev.h"
/* 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"
/* 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"
#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"
#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;
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");
}
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");
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;
}
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);
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);
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;
}
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;
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;
bool remote_csum_tx;
bool remote_csum_rx;
bool group_policy;
+ bool generic_protocol_extension;
+ bool inherit;
struct ifla_vxlan_port_range port_range;
};
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);
WireguardPeer *peer;
Wireguard *w;
Iterator i;
- int r = 0;
+ int r;
assert(netdev);
w = WIREGUARD(netdev);
typedef struct Wireguard Wireguard;
+#include <netinet/in.h>
#include <linux/wireguard.h>
#include "in-addr-util.h"
unsigned short iftype;
struct ether_addr mac_address;
uint32_t mtu;
+ uint32_t min_mtu;
+ uint32_t max_mtu;
+ uint32_t tx_queues;
+ uint32_t rx_queues;
bool has_mac_address:1;
bool has_mtu:1;
+ bool has_min_mtu:1;
+ bool has_max_mtu:1;
+ bool has_tx_queues:1;
+ bool has_rx_queues:1;
} LinkInfo;
static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0;
info->has_mtu =
- sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) &&
+ sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) >= 0 &&
info->mtu > 0;
+ info->has_min_mtu =
+ sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu) >= 0 &&
+ info->min_mtu > 0;
+
+ info->has_max_mtu =
+ sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu) >= 0 &&
+ info->min_mtu > 0;
+
+ info->has_rx_queues =
+ sd_netlink_message_read_u32(m, IFLA_NUM_RX_QUEUES, &info->rx_queues) >= 0 &&
+ info->rx_queues > 0;
+
+ info->has_tx_queues =
+ sd_netlink_message_read_u32(m, IFLA_NUM_TX_QUEUES, &info->tx_queues) >= 0 &&
+ info->tx_queues > 0;
+
return 1;
}
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;
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;
printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off_color_operational, info->ifindex, info->name);
- printf(" Link File: %s\n"
- " Network File: %s\n"
- " Type: %s\n"
- " State: %s%s%s (%s%s%s)\n",
+ printf(" Link File: %s\n"
+ " Network File: %s\n"
+ " Type: %s\n"
+ " State: %s%s%s (%s%s%s)\n",
strna(link),
strna(network),
strna(t),
on_color_setup, strna(setup_state), off_color_setup);
if (path)
- printf(" Path: %s\n", path);
+ printf(" Path: %s\n", path);
if (driver)
- printf(" Driver: %s\n", driver);
+ printf(" Driver: %s\n", driver);
if (vendor)
- printf(" Vendor: %s\n", vendor);
+ printf(" Vendor: %s\n", vendor);
if (model)
- printf(" Model: %s\n", model);
+ printf(" Model: %s\n", model);
if (info->has_mac_address) {
_cleanup_free_ char *description = NULL;
(void) ieee_oui(hwdb, &info->mac_address, &description);
if (description)
- printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description);
+ printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description);
else
- printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea));
+ printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea));
}
if (info->has_mtu)
- printf(" MTU: %" PRIu32 "\n", info->mtu);
+ printf(" MTU: %" PRIu32 "\n", info->mtu);
+ if (info->has_min_mtu)
+ printf(" Minimum MTU: %" PRIu32 "\n", info->min_mtu);
+ if (info->has_max_mtu)
+ printf(" Maximum MTU: %" PRIu32 "\n", info->max_mtu);
+
+ if (info->has_tx_queues)
+ printf("Transmit Queue Length: %" PRIu32 "\n", info->tx_queues);
+ if (info->has_rx_queues)
+ printf(" Receive Queue Length: %" PRIu32 "\n", info->rx_queues);
- (void) dump_addresses(rtnl, " Address: ", info->ifindex);
- (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex);
+ (void) dump_addresses(rtnl, " Address: ", info->ifindex);
+ (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex);
- dump_list(" DNS: ", dns);
- dump_list(" Search Domains: ", search_domains);
- dump_list(" Route Domains: ", route_domains);
+ dump_list(" DNS: ", dns);
+ dump_list(" Search Domains: ", search_domains);
+ dump_list(" Route Domains: ", route_domains);
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
dump_ifindexes("Carrier Bound To: ", carrier_bound_to);
dump_ifindexes("Carrier Bound By: ", carrier_bound_by);
(void) sd_network_link_get_timezone(info->ifindex, &tz);
if (tz)
- printf(" Time Zone: %s\n", tz);
+ printf(" Time Zone: %s\n", tz);
- (void) dump_lldp_neighbors(" Connected To: ", info->ifindex);
+ (void) dump_lldp_neighbors(" Connected To: ", info->ifindex);
return 0;
}
static int run(int argc, char* argv[]) {
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
}
/* 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) {
}
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;
--- /dev/null
+/* 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;
+
+ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ 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;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+typedef struct Link Link;
+
+int link_configure_can(Link *link);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/if.h>
#include "alloc-util.h"
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");
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);
}
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_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4)) {
+ 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;
}
}
r = dhcp_lease_acquired(client, link);
if (r < 0) {
link_enter_failed(link);
- return;
+ return r;
}
}
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");
break;
}
- return;
+ return 0;
}
static int dhcp4_set_hostname(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);
}
Copyright © 2014 Intel Corporation. All rights reserved.
***/
-#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/if.h>
#include "sd-radv.h"
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;
&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;
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);
strnull(assigned_buf),
strnull(buf), pd_prefix_len);
- if (assigned_link == NULL)
+ if (!assigned_link)
continue;
} else
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);
&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);
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");
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);
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;
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)
#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,
*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);
/* 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);
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");
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;
+}
Copyright © 2014 Intel Corporation. All rights reserved.
***/
+#include <linux/neighbour.h>
+
#include "conf-parser.h"
#include "list.h"
#include "macro.h"
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);
};
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);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/if.h>
#include "network-internal.h"
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)
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);
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);
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);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/if.h>
#include <unistd.h>
/* 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"
#include "fd-util.h"
#include "fileio.h"
#include "missing_network.h"
+#include "netdev/bond.h"
+#include "netdev/bridge.h"
+#include "netdev/ipvlan.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"
if (link->network->bond)
return false;
+ if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ return false;
+
if (manager_sysctl_ipv6_enabled(link->manager) == 0)
return false;
if (link->network->bond)
return false;
+ if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ return false;
+
return link->network->dhcp & ADDRESS_FAMILY_IPV4;
}
if (link->network->bond)
return false;
+ if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ return false;
+
return link->network->dhcp_server;
}
-static bool link_ipv4ll_enabled(Link *link) {
+bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
assert(link);
+ assert((mask & ~(ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) == 0);
if (link->flags & IFF_LOOPBACK)
return false;
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", "can", "vcan", "vxcan"))
+ return false;
+
+ /* L3 or L3S mode do not support ARP. */
+ if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
return false;
if (link->network->bond)
return false;
- return link->network->link_local & ADDRESS_FAMILY_IPV4;
+ return link->network->link_local & mask;
}
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", "can", "vcan", "vxcan"))
return false;
if (link->network->bond)
if (manager_sysctl_ipv6_enabled(link->manager) == 0)
return false;
+ if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ return false;
+
/* DHCPv6 client will not be started if no IPv6 link-local address is configured. */
return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network);
}
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);
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));
/* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */
return true;
- if (!link->enslaved_raw)
- return false;
-
if (!link->network)
return false;
- if (link->network->bridge)
- /* TODO: support the case when link is not managed by networkd. */
+ if (link->master_ifindex > 0 && link->network->bridge)
return true;
+ /* TODO: add conditions for other netdevs. */
+
return false;
}
? ((old & flag) ? (" -" string) : (" +" string)) \
: "")
-static int link_update_flags(Link *link, sd_netlink_message *m) {
+static int link_update_flags(Link *link, sd_netlink_message *m, bool force_update_operstate) {
unsigned flags, unknown_flags_added, unknown_flags_removed, unknown_flags;
uint8_t operstate;
int r;
the state was unchanged */
operstate = link->kernel_operstate;
- if ((link->flags == flags) && (link->kernel_operstate == operstate))
+ if (!force_update_operstate && (link->flags == flags) && (link->kernel_operstate == operstate))
return 0;
if (link->flags != flags) {
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;
if (r < 0)
return r;
- r = link_update_flags(link, message);
+ r = link_update_flags(link, message, false);
if (r < 0)
return r;
}
static Link *link_free(Link *link) {
- Link *carrier;
Address *address;
- Route *route;
- Iterator i;
assert(link);
- while ((route = set_first(link->routes)))
- 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);
- while ((route = set_first(link->routes_foreign)))
- route_free(route);
-
- 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);
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);
set_free_with_destructor(link->slaves, link_unref);
+ network_unref(link->network);
+
return mfree(link);
}
return 0;
}
-static void link_set_state(Link *link, LinkState state) {
+void link_set_state(Link *link, LinkState state) {
assert(link);
if (link->state == state)
static void link_enter_unmanaged(Link *link) {
assert(link);
- log_link_debug(link, "Unmanaged");
-
link_set_state(link, LINK_STATE_UNMANAGED);
link_dirty(link);
}
-static int link_stop_clients(Link *link) {
+int link_stop_clients(Link *link) {
int r = 0, k;
assert(link);
if (!link->addresses_ready) {
link->addresses_ready = true;
link_request_set_routes(link);
+ return;
}
if (!link->static_routes_configured)
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, ADDRESS_FAMILY_IPV4) && !(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_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && 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)
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) {
}
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)
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) {
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;
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);
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;
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_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;
assert(link->manager);
assert(link->manager->event);
- if (link_ipv4ll_enabled(link)) {
+ if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4)) {
assert(link->ipv4ll);
log_link_debug(link, "Acquiring IPv4 link-local address");
if (link->kernel_operstate == IF_OPER_UNKNOWN)
/* operstate may not be implemented, so fall back to flags */
- if ((link->flags & IFF_LOWER_UP) && !(link->flags & IFF_DORMANT))
+ if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) &&
+ !FLAGS_SET(link->flags, IFF_DORMANT))
return true;
return false;
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);
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 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)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
- link_ref(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;
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;
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");
}
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) {
assert(link);
assert(link->network);
assert(link->enslaving > 0);
- assert(!link->enslaved_raw);
link->enslaving--;
log_link_debug(link, "Joined netdev");
if (link->enslaving == 0) {
- link->enslaved_raw = true;
link_joined(link);
}
link_dirty(link);
link->enslaving = 0;
- link->enslaved_raw = false;
if (link->network->bond) {
if (link->network->bond->state == NETDEV_STATE_READY &&
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->network);
assert(link->state == LINK_STATE_INITIALIZED);
- if (STRPTR_IN_SET(link->kind, "can", "vcan"))
+ if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
return link_configure_can(link);
/* Drop foreign config, but ignore loopback or critical devices.
if (r < 0)
return r;
- if (link_ipv4ll_enabled(link)) {
+ if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) {
r = ipv4ll_configure(link);
if (r < 0)
return r;
}
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);
}
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);
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);
const char *ifname;
uint32_t mtu;
bool had_carrier, carrier_gained, carrier_lost;
- int r;
+ int old_master, r;
assert(link);
assert(link->ifname);
}
}
+ old_master = link->master_ifindex;
+ (void) sd_netlink_message_read_u32(m, IFLA_MASTER, (uint32_t *) &link->master_ifindex);
+
had_carrier = link_has_carrier(link);
- r = link_update_flags(link, m);
+ r = link_update_flags(link, m, old_master != link->master_ifindex);
if (r < 0)
return r;
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));
(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;
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;
#include "list.h"
#include "log-link.h"
#include "network-util.h"
+#include "networkd-util.h"
#include "set.h"
typedef enum LinkState {
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned enslaving;
- /* link_is_enslaved() has additional checks. So, it is named _raw. */
- bool enslaved_raw;
Set *addresses;
Set *addresses_foreign;
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);
+void link_set_state(Link *link, LinkState state);
void link_check_ready(Link *link);
void link_update_operstate(Link *link, bool also_update_bond_master);
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, AddressFamilyBoolean mask);
+
int dhcp4_configure(Link *link);
int dhcp4_set_client_identifier(Link *link);
int dhcp4_set_promote_secondaries(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_;
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
#include <endian.h>
#include <inttypes.h>
+#include <net/if.h>
#include <string.h>
#include "alloc-util.h"
#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"
[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);
/* 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,
_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);
/* 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"
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;
}
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);
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;
}
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);
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;
return 0;
}
-static void dhcp6_prefixes_hash_func(const struct in6_addr *addr, struct siphash *state) {
- assert(addr);
-
- siphash24_compress(addr, sizeof(*addr), state);
-}
-
-static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
- return memcmp(a, b, sizeof(*a));
-}
-
-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;
(void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
- r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops);
+ 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, &in6_addr_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->dhcp6_prefixes, a, link);
if (r < 0)
return r;
- return hashmap_put(m->dhcp6_prefixes, addr, link);
+ TAKE_PTR(a);
+ link_ref(link);
+ return 0;
}
static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
}
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;
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;
}
if (r < 0)
return r;
- LIST_HEAD_INIT(m->networks);
-
r = sd_resolve_default(&m->resolve);
if (r < 0)
return r;
}
void manager_free(Manager *m) {
+ struct in6_addr *a;
AddressPool *pool;
- Network *network;
Link *link;
if (!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->links = hashmap_free_with_destructor(m->links, link_unref);
m->duids_requesting_uuid = set_free(m->duids_requesting_uuid);
- while ((network = m->networks))
- network_free(network);
- hashmap_free(m->networks_by_name);
+ m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
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);
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <arpa/inet.h>
-
#include "sd-bus.h"
#include "sd-device.h"
#include "sd-event.h"
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;
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;
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)
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;
return 0;
}
+static int ndisc_prefix_is_black_listed(Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
+ union in_addr_union a;
+ uint8_t type;
+
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
+ if (r == 0) /* EOF */
+ return false;
+
+ r = sd_ndisc_router_option_get_type(rt, &type);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
+
+ if (type != SD_NDISC_OPTION_PREFIX_INFORMATION)
+ continue;
+
+ r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+
+ if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *b = NULL;
+
+ (void) in_addr_to_string(AF_INET6, &a, &b);
+ log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
+ }
+
+ return true;
+ }
+ }
+}
+
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
uint64_t flags;
- int r = 0;
+ int r;
assert(link);
assert(link->network);
}
}
- (void) ndisc_router_process_default(link, rt);
- (void) ndisc_router_process_options(link, rt);
+ if (ndisc_prefix_is_black_listed(link, rt) == 0) {
+ (void) ndisc_router_process_default(link, rt);
+ (void) ndisc_router_process_options(link, rt);
+ }
return r;
}
link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
}
+
+int config_parse_ndisc_black_listed_prefix(
+ 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->ndisc_black_listed_prefix = set_free_free(network->ndisc_black_listed_prefix);
+ return 0;
+ }
+
+ for (p = rvalue;;) {
+ _cleanup_free_ char *n = NULL;
+ _cleanup_free_ struct in6_addr *a = 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 NDISC black listed prefix, ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = in_addr_from_string(AF_INET6, n, &ip);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "NDISC black listed prefix is invalid, ignoring assignment: %s", n);
+ continue;
+ }
+
+ r = set_ensure_allocated(&network->ndisc_black_listed_prefix, &in6_addr_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ a = newdup(struct in6_addr, &ip.in6, 1);
+ if (!a)
+ return log_oom();
+
+ r = set_put(network->ndisc_black_listed_prefix, a);
+ if (r < 0) {
+ if (r == -EEXIST)
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "NDISC black listed prefixs is duplicated, ignoring assignment: %s", n);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n);
+ continue;
+ }
+
+ TAKE_PTR(a);
+ }
+
+ return 0;
+}
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "conf-parser.h"
#include "networkd-link.h"
#include "time-util.h"
int ndisc_configure(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix);
_cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
Network *network;
+ Iterator i;
int r;
assert(bus);
assert(m);
assert(nodes);
- LIST_FOREACH(networks, network, m->networks) {
+ ORDERED_HASHMAP_FOREACH(network, m->networks, i) {
char *p;
p = network_bus_path(network);
#include "conf-parser.h"
#include "network-internal.h"
#include "networkd-conf.h"
+#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "vlan-util.h"
%}
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)
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)
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
+Route.TTLPropagate, config_parse_route_ttl_propagate, 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)
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)
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)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0
+IPv6AcceptRA.BlackList, config_parse_ndisc_black_listed_prefix, 0, 0
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
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
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <net/if.h>
+#include <netinet/in.h>
#include <linux/netdevice.h>
#include "alloc-util.h"
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;
AddressLabel *label, *label_next;
Prefix *prefix, *prefix_next;
RoutingPolicyRule *rule, *rule_next;
- uint32_t mtu;
assert(network);
assert(network->filename);
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;
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;
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;
.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,
/* 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,
.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,
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;
}
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;
return 0;
}
-void network_free(Network *network) {
+static Network *network_free(Network *network) {
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
RoutingPolicyRule *rule;
FdbEntry *fdb_entry;
Route *route;
if (!network)
- return;
+ return NULL;
free(network->filename);
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);
ordered_set_free_free(network->router_search_domains);
free(network->router_dns);
+ set_free_free(network->ndisc_black_listed_prefix);
free(network->bridge_name);
free(network->bond_name);
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);
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;
assert(name);
assert(ret);
- network = hashmap_get(manager->networks_by_name, name);
+ network = ordered_hashmap_get(manager->networks, name);
if (!network)
return -ENOENT;
Network **ret) {
const char *path = NULL, *driver = NULL, *devtype = NULL;
Network *network;
+ Iterator i;
assert(manager);
assert(ret);
(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,
*ret = network;
return 0;
}
- }
*ret = NULL;
assert(network);
assert(link);
- link->network = network;
+ link->network = network_ref(network);
if (network->n_dns > 0 ||
!strv_isempty(network->ntp) ||
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,
void *userdata) {
AddressFamilyBoolean *link_local = data;
+ int r;
assert(filename);
assert(lvalue);
* 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;
}
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)
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;
}
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;
}
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)
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;
}
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;
}
}
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 < 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");
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,
#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"
_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;
char *filename;
char *name;
+ unsigned n_ref;
+
Set *match_mac;
char **match_path;
char **match_driver;
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;
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;
AddressFamilyBoolean link_local;
bool ipv4ll_route;
+ bool default_route_on_device;
+
/* IPv6 prefix delegation support */
RADVPrefixDelegation router_prefix_delegation;
usec_t router_lifetime_usec;
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;
DHCPUseDomains ipv6_accept_ra_use_domains;
uint32_t ipv6_accept_ra_route_table;
bool ipv6_accept_ra_route_table_set;
+ Set *ndisc_black_listed_prefix;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
struct ether_addr *mac;
uint32_t mtu;
- bool mtu_is_set; /* Indicate MTUBytes= is specified. */
int arp;
int multicast;
int allmulticast;
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);
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);
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_;
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;
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;
}
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;
}
.table = RT_TABLE_MAIN,
.lifetime = USEC_INFINITY,
.quickack = -1,
+ .fast_open_no_cookie = -1,
.gateway_onlink = -1,
+ .ttl_propagate = -1,
};
*ret = TAKE_PTR(route);
return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
}
+ if (route->ttl_propagate >= 0) {
+ r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
+ }
+
r = sd_netlink_message_open_container(req, RTA_METRICS);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
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");
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,
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,
return 0;
}
+int config_parse_route_ttl_propagate(
+ 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 = userdata;
+ _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+ int r, k;
+
+ 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 TTLPropagate= value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n->ttl_propagate = k;
+
+ TAKE_PTR(n);
+ return 0;
+}
+
int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;
int family;
int quickack;
+ int fast_open_no_cookie;
+ int ttl_propagate;
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
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);
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_ttl_propagate);
CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
#pragma once
#include <inttypes.h>
+#include <netinet/in.h>
#include <linux/fib_rules.h>
#include <stdbool.h>
#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,
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;
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);
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/types.h>
#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);
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;
}
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <arpa/inet.h>
#include <sys/param.h>
#include "sd-device.h"
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) {
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... */
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));
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));
}
}
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);
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;
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))
unsigned i = 0;
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
[['src/nspawn/test-nspawn-tables.c'],
[libnspawn_core,
libshared],
- []],
+ [libseccomp]],
[['src/nspawn/test-patch-uid.c'],
[libnspawn_core,
const char *name;
uint32_t action;
} table[] = {
- { "SCMP_ACT_ALLOW", SCMP_ACT_ALLOW },
- { "SCMP_ACT_ERRNO", SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */
- { "SCMP_ACT_KILL", SCMP_ACT_KILL },
+ { "SCMP_ACT_ALLOW", SCMP_ACT_ALLOW },
+ { "SCMP_ACT_ERRNO", SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */
+ { "SCMP_ACT_KILL", SCMP_ACT_KILL },
+#ifdef SCMP_ACT_KILL_PROCESS
+ { "SCMP_ACT_KILL_PROCESS", SCMP_ACT_KILL_PROCESS },
+#endif
+#ifdef SCMP_ACT_KILL_THREAD
+ { "SCMP_ACT_KILL_THREAD", SCMP_ACT_KILL_THREAD },
+#endif
#ifdef SCMP_ACT_LOG
- { "SCMP_ACT_LOG", SCMP_ACT_LOG },
+ { "SCMP_ACT_LOG", SCMP_ACT_LOG },
#endif
- { "SCMP_ACT_TRAP", SCMP_ACT_TRAP },
+ { "SCMP_ACT_TRAP", SCMP_ACT_TRAP },
/* We don't support SCMP_ACT_TRACE because that requires a tracer, and that doesn't really make sense
* here */
static int run(int argc, char* argv[]) {
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
int r;
+ log_show_color(true);
log_parse_environment();
log_open();
r = add_symlink("rc-local.service", "multi-user.target");
}
- if (check_executable(RC_LOCAL_SCRIPT_PATH_STOP) >= 0) {
- log_debug("Automatically adding halt-local.service.");
-
- k = add_symlink("halt-local.service", "final.target");
- }
-
return r < 0 ? r : k;
}
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
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 it is. The service manager will trigger the service right-away
- * then, but everything is discoverable as usual. Moreover, the server side
- * might have a different clock or timezone than we do, hence it should
- * decide when or whether to run something. */
+ /* 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");
_cleanup_free_ char *description = NULL;
int r, retval = EXIT_SUCCESS;
+ log_show_color(true);
log_parse_environment();
log_open();
if (n < m)
break;
- explicit_bzero_safe(p, n);
+ explicit_bzero_safe(p, m);
if (m > LONG_MAX / 2) /* overflow check */
return -ENOMEM;
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);
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))
int r;
assert(p);
- assert(spec);
c = new(CalendarSpec, 1);
if (!c)
if (!calendar_spec_valid(c))
return -EINVAL;
- *spec = TAKE_PTR(c);
+ if (spec)
+ *spec = TAKE_PTR(c);
return 0;
}
pid_t pids[],
unsigned n_pids,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
bool extra,
bool more,
OutputFlags flags) {
pid_width = DECIMAL_STR_WIDTH(pids[j]);
if (flags & OUTPUT_FULL_WIDTH)
- n_columns = 0;
+ n_columns = SIZE_MAX;
else {
- if (n_columns > pid_width+2)
- n_columns -= pid_width+2;
+ if (n_columns > pid_width + 3) /* something like "├─1114784 " */
+ n_columns -= pid_width + 3;
else
n_columns = 20;
}
for (i = 0; i < n_pids; i++) {
_cleanup_free_ char *t = NULL;
- (void) get_process_cmdline(pids[i], n_columns, true, &t);
+ (void) get_process_cmdline(pids[i], n_columns,
+ PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_USE_LOCALE,
+ &t);
if (extra)
printf("%s%s ", prefix, special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
static int show_cgroup_one_by_path(
const char *path,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
bool more,
OutputFlags flags) {
int show_cgroup_by_path(
const char *path,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
OutputFlags flags) {
_cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
int show_cgroup(const char *controller,
const char *path,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
OutputFlags flags) {
_cleanup_free_ char *p = NULL;
int r;
const char *controller,
const char *path,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
const pid_t pids[],
unsigned n_pids,
OutputFlags flags) {
const char *controller,
const char *path,
const char *prefix,
- unsigned n_columns,
+ size_t n_columns,
const pid_t extra_pids[],
unsigned n_extra_pids,
OutputFlags flags) {
#include "logs-show.h"
#include "output-mode.h"
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags);
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags);
+int show_cgroup_by_path(const char *path, const char *prefix, size_t n_columns, OutputFlags flags);
+int show_cgroup(const char *controller, const char *path, const char *prefix, size_t n_columns, OutputFlags flags);
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, size_t n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
int show_cgroup_get_unit_path_and_warn(
sd_bus *bus,
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;
if (path)
*path = TAKE_PTR(p);
if (active)
- *active = !!(header->attr & LOAD_OPTION_ACTIVE);
+ *active = header->attr & LOAD_OPTION_ACTIVE;
return 0;
}
if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */
return false;
- return in_charset(s, ALPHANUMERICAL "-_.");
+ return in_charset(s, ALPHANUMERICAL "+-_.");
}
char *efi_tilt_backslashes(char *s) {
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);
c = memchr(imp->buf + start, '\n',
imp->filled - start);
- if (c != NULL)
+ if (c)
break;
}
column = &column_buffer;
for (;;) {
+ _cleanup_(json_variant_unrefp) JsonVariant *add = NULL;
_cleanup_free_ char *string = NULL;
unsigned line_token, column_token;
- JsonVariant *add = NULL;
JsonStack *current;
JsonValue value;
int token;
goto finish;
}
- current->elements[current->n_elements++] = add;
+ current->elements[current->n_elements++] = TAKE_PTR(add);
}
}
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdio.h>
+
/* This needs to be after sys/mount.h */
#include <libmount.h>
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;
+}
unsigned n_columns,
OutputFlags flags,
int priority,
+ bool audit,
const char* message,
size_t message_len,
size_t highlight[2]) {
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;
+ get_log_colors(priority, &color_on, &color_off, &highlight_on);
+
+ if (audit && strempty(color_on)) {
+ color_on = ANSI_BLUE;
color_off = ANSI_NORMAL;
- highlight_on = ANSI_HIGHLIGHT_RED;
}
}
const void *data;
size_t length;
size_t n = 0;
- _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *unit = NULL, *user_unit = NULL;
- size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, unit_len = 0, user_unit_len = 0;
+ _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *unit = NULL, *user_unit = NULL;
+ size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, unit_len = 0, user_unit_len = 0;
int p = LOG_INFO;
- bool ellipsized = false;
+ bool ellipsized = false, audit;
const ParseFieldVec fields[] = {
PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
+ PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len),
PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
+
+ audit = streq_ptr(transport, "audit");
+
if (mode == OUTPUT_SHORT_MONOTONIC)
r = output_timestamp_monotonic(f, j, monotonic);
else
} else {
fputs(": ", f);
ellipsized |=
- print_multiline(f, n + 2, n_columns, flags, p,
+ print_multiline(f, n + 2, n_columns, flags, p, audit,
message, message_len,
highlight_shifted);
}
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
&& utf8_is_printable(data, length))) {
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
- print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL);
+ print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
+ c + 1, length - fieldlen - 1,
+ NULL);
fputs(off, f);
} else {
char bytes[FORMAT_BYTES_MAX];
_cleanup_close_ int control = -1, loop = -1;
_cleanup_free_ char *loopdev = NULL;
+ unsigned n_attempts = 0;
struct stat st;
LoopDevice *d;
int nr, r;
if (control < 0)
return -errno;
- nr = ioctl(control, LOOP_CTL_GET_FREE);
- if (nr < 0)
- return -errno;
+ /* Loop around LOOP_CTL_GET_FREE, since at the moment we attempt to open the returned device it might
+ * be gone already, taken by somebody else racing against us. */
+ for (;;) {
+ nr = ioctl(control, LOOP_CTL_GET_FREE);
+ if (nr < 0)
+ return -errno;
- if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
- return -ENOMEM;
+ if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
+ return -ENOMEM;
- loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
- if (loop < 0)
- return -errno;
+ loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+ if (loop < 0)
+ return -errno;
+ if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
+ if (errno != EBUSY)
+ return -errno;
- if (ioctl(loop, LOOP_SET_FD, fd) < 0)
- return -errno;
+ if (++n_attempts >= 64) /* Give up eventually */
+ return -EBUSY;
+ } else
+ break;
+
+ loopdev = mfree(loopdev);
+ loop = safe_close(loop);
+ }
if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
return -errno;
const struct stat *st,
Image **ret) {
- _cleanup_free_ char *pretty_buffer = NULL;
+ _cleanup_free_ char *pretty_buffer = NULL, *parent = NULL;
struct stat stbuf;
bool read_only;
int r;
assert(dfd >= 0 || dfd == AT_FDCWD);
+ assert(path || dfd == AT_FDCWD);
assert(filename);
/* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block
st = &stbuf;
}
+ if (!path) {
+ if (dfd == AT_FDCWD)
+ (void) safe_getcwd(&parent);
+ else
+ (void) fd_get_path(dfd, &parent);
+ }
+
read_only =
(path && path_startswith(path, "/usr")) ||
(faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
if (block_fd < 0)
- log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename);
+ log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
else {
/* Refresh stat data after opening the node */
if (fstat(block_fd, &stbuf) < 0)
int state = 0;
if (ioctl(block_fd, BLKROGET, &state) < 0)
- log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename);
+ log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
else if (state)
read_only = true;
}
if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
- log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path, filename);
+ log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
block_fd = safe_close(block_fd);
}
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;
uid-range.c
uid-range.h
utmp-wtmp.h
+ varlink.c
+ varlink.h
verbs.c
verbs.h
vlan-util.c
#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"
* 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++;
} while (again);
- return r < 0 ? r : n;
+ return n;
}
static int get_mount_flags(const char *path, unsigned long *flags) {
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;
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;
}
}
* 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;
}
return 0;
}
+
+uint32_t scmp_act_kill_process(void) {
+
+ /* Returns SCMP_ACT_KILL_PROCESS if it's supported, and SCMP_ACT_KILL_THREAD otherwise. We never
+ * actually want to use SCMP_ACT_KILL_THREAD as its semantics are nuts (killing arbitrary threads of
+ * a program is just a bad idea), but on old kernels/old libseccomp it is all we have, and at least
+ * for single-threaded apps does the right thing. */
+
+#ifdef SCMP_ACT_KILL_PROCESS
+ if (seccomp_api_get() >= 3)
+ return SCMP_ACT_KILL_PROCESS;
+#endif
+
+ return SCMP_ACT_KILL; /* same as SCMP_ACT_KILL_THREAD */
+}
DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release);
int parse_syscall_archs(char **l, Set **archs);
+
+uint32_t scmp_act_kill_process(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;
}
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;
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) {
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)
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)
--- /dev/null
+/* 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);
+
+ /* Here use new0 as the below structured initializer is nested. */
+ v = new0(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;
+ const size_t new_size = v->output_buffer_size + r + 1;
+
+ n = new(char, new_size);
+ 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_allocated = v->output_buffer_size = new_size;
+ 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);
+}
--- /dev/null
+/* 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"
}
static void sync_with_progress(void) {
- unsigned long long dirty = ULONG_LONG_MAX;
+ unsigned long long dirty = ULLONG_MAX;
unsigned checks;
pid_t pid;
int r;
}
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);
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)
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;
}
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 {
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,
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);
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);
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);
/* 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;
***/
#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"
[],
[]],
+ [['src/test/test-umask-util.c'],
+ [],
+ []],
+
[['src/test/test-proc-cmdline.c'],
[],
[]],
libmount,
libblkid]],
+ [['src/test/test-varlink.c'],
+ [],
+ [threads]],
+
[['src/test/test-cgroup-util.c'],
[],
[]],
]
-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'],
[],
/* 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] = { };
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) {
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;
}
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;
m->default_tasks_accounting = false;
m->default_tasks_max = (uint64_t) -1;
- assert_se(r >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
/* Load units and verify hierarchy. */
* 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 │
* │ 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 │
* └───────────────────────────────┘ └─────────────────────────────────┘ └────────────────────────────────┘
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);
#include "tests.h"
static void test_cescape(void) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *t;
- assert_se(escaped = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
- assert_se(streq(escaped, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+ assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
+ assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+}
+
+static void test_xescape(void) {
+ _cleanup_free_ char *t;
+
+ assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", ""));
+ assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb"));
+}
+
+static void test_xescape_full(bool eight_bits) {
+ const char* escaped = !eight_bits ?
+ "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb" :
+ "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\177\234\313";
+ const unsigned full_fit = !eight_bits ? 55 : 46;
+
+ for (unsigned i = 0; i < 60; i++) {
+ _cleanup_free_ char *t;
+
+ assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, eight_bits));
+
+ log_info("%02d: %s", i, t);
+
+ if (i >= full_fit)
+ assert_se(streq(t, escaped));
+ else if (i >= 3) {
+ /* We need up to four columns, so up to three three columns may be wasted */
+ assert_se(strlen(t) == i || strlen(t) == i - 1 || strlen(t) == i - 2 || strlen(t) == i - 3);
+ assert_se(strneq(t, escaped, i - 3) || strneq(t, escaped, i - 4) ||
+ strneq(t, escaped, i - 5) || strneq(t, escaped, i - 6));
+ assert_se(endswith(t, "..."));
+ } else {
+ assert_se(strlen(t) == i);
+ assert_se(strneq(t, "...", i));
+ }
+ }
}
static void test_cunescape(void) {
test_setup_logging(LOG_DEBUG);
test_cescape();
+ test_xescape();
+ test_xescape_full(false);
+ test_xescape_full(true);
test_cunescape();
test_shell_escape();
test_shell_maybe_quote();
typedef void (*test_function_t)(Manager *m);
+static int cld_dumped_to_killed(int code) {
+ /* Depending on the system, seccomp version, … some signals might result in dumping, others in plain
+ * killing. Let's ignore the difference here, and map both cases to CLD_KILLED */
+ return code == CLD_DUMPED ? CLD_KILLED : code;
+}
+
static void check(const char *func, Manager *m, Unit *unit, int status_expected, int code_expected) {
Service *service = NULL;
usec_t ts;
}
}
exec_status_dump(&service->main_exec_status, stdout, "\t");
- if (service->main_exec_status.status != status_expected) {
- log_error("%s: %s: exit status %d, expected %d",
+
+ if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
+ log_error("%s: %s: exit code %d, expected %d",
func, unit->id,
- service->main_exec_status.status, status_expected);
+ service->main_exec_status.code, code_expected);
abort();
}
- if (service->main_exec_status.code != code_expected) {
- log_error("%s: %s: exit code %d, expected %d",
+
+ if (service->main_exec_status.status != status_expected) {
+ log_error("%s: %s: exit status %d, expected %d",
func, unit->id,
- service->main_exec_status.code, code_expected);
+ service->main_exec_status.status, status_expected);
abort();
}
}
_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;
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));
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);
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);
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);
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);
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");
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);
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);
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,
- 0);
+ 0,
+ NULL);
if (r < 0) {
log_error_errno(r, "Failed to setup namespace: %m");
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);
} else
log_warning("%s not exist.", path);
- assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
+ assert_se(get_process_cmdline(pid, 0, PROCESS_CMDLINE_COMM_FALLBACK, &c) >= 0);
log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
- assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
+ assert_se(get_process_cmdline(pid, 8, 0, &d) >= 0);
log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
free(d);
- assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
+ assert_se(get_process_cmdline(pid, 1, 0, &d) >= 0);
log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
assert_se(get_process_ppid(pid, &e) >= 0);
test_get_process_comm_escape_one("foo", "foo");
test_get_process_comm_escape_one("012345678901234", "012345678901234");
test_get_process_comm_escape_one("0123456789012345", "012345678901234");
- test_get_process_comm_escape_one("äöüß", "\\303\\244\\303…");
- test_get_process_comm_escape_one("xäöüß", "x\\303\\244…");
- test_get_process_comm_escape_one("xxäöüß", "xx\\303\\244…");
- test_get_process_comm_escape_one("xxxäöüß", "xxx\\303\\244…");
- test_get_process_comm_escape_one("xxxxäöüß", "xxxx\\303\\244…");
- test_get_process_comm_escape_one("xxxxxäöüß", "xxxxx\\303…");
+ test_get_process_comm_escape_one("äöüß", "\\303\\244\\303\\266\\303\\274\\303\\237");
+ test_get_process_comm_escape_one("xäöüß", "x\\303\\244\\303\\266\\303\\274\\303\\237");
+ test_get_process_comm_escape_one("xxäöüß", "xx\\303\\244\\303\\266\\303\\274\\303\\237");
+ test_get_process_comm_escape_one("xxxäöüß", "xxx\\303\\244\\303\\266\\303\\274\\303\\237");
+ test_get_process_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237");
+ test_get_process_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237");
assert_se(prctl(PR_SET_NAME, saved) >= 0);
}
assert_se(prctl(PR_SET_NAME, "testa") >= 0);
- assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
- assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ log_info("'%s'", line);
assert_se(streq(line, ""));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
- assert_se(streq(line, "["));
+ assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
- assert_se(streq(line, "[."));
+ assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
- assert_se(streq(line, "[.."));
+ assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[t…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
- assert_se(streq(line, "[..."));
+ assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[te…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
- assert_se(streq(line, "[...]"));
+ assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[tes…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
- assert_se(streq(line, "[t...]"));
+ assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[test…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
- assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
-
- assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
-
- assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
- assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
+ assert_se(write(fd, "foo\0bar", 8) == 8);
- assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
+ log_info("'%s'", line);
assert_se(streq(line, "foo bar"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar"));
line = mfree(line);
assert_se(write(fd, "quux", 4) == 4);
- assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
+ log_info("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
- assert_se(streq(line, ""));
+ assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
- assert_se(streq(line, "."));
+ assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "f…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
- assert_se(streq(line, ".."));
+ assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "fo…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
- assert_se(streq(line, "..."));
+ assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
- assert_se(streq(line, "f..."));
+ assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo …"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
- assert_se(streq(line, "fo..."));
+ assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo b…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
- assert_se(streq(line, "foo..."));
+ assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo ba…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
- assert_se(streq(line, "foo..."));
+ assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo bar…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
- assert_se(streq(line, "foo b..."));
+ assert_se(get_process_cmdline(getpid_cached(), 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo bar …"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
- assert_se(streq(line, "foo ba..."));
+ assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo bar q…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
- assert_se(streq(line, "foo bar..."));
+ assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo bar qu…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
- assert_se(streq(line, "foo bar..."));
+ assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "foo bar quux"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(ftruncate(fd, 0) >= 0);
assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
- assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
- assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+ assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[aaaa bbbb cccc]"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
- assert_se(streq(line, "[aaaa...]"));
+ assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[aaaa bbb…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
- assert_se(streq(line, "[aaaa...]"));
+ assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[aaaa bbbb…"));
line = mfree(line);
- assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
- assert_se(streq(line, "[aaaa b...]"));
+ assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+ assert_se(streq(line, "[aaaa bbbb …"));
line = mfree(line);
safe_close(fd);
assert_se(get_process_comm(0, &comm) >= 0);
log_info("comm = <%s>", comm);
assert_se(strneq(comm, p, TASK_COMM_LEN-1));
+ /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
+ * future. We'd only check the initial part, at least until we recompile, but this will still pass. */
- r = get_process_cmdline(0, 0, false, &cmdline);
+ r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline);
assert_se(r >= 0);
/* we cannot expect cmdline to be renamed properly without privileges */
if (geteuid() == 0) {
assert_se(access("/", F_OK) >= 0);
assert_se(poll(NULL, 0, 0) == 0);
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL, true) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, scmp_act_kill_process(), true) >= 0);
assert_se(access("/", F_OK) >= 0);
assert_se(poll(NULL, 0, 0) == 0);
assert_se(PATH_IN_SET(ttyname, "ptmx", "pts/ptmx"));
}
+static void test_one_color(const char *name, const char *color) {
+ printf("<%s%s%s>\n", color, name, ansi_normal());
+}
+
+static void test_colors(void) {
+ log_info("/* %s */", __func__);
+
+ test_one_color("normal", ansi_normal());
+ test_one_color("highlight", ansi_highlight());
+ test_one_color("red", ansi_red());
+ test_one_color("green", ansi_green());
+ test_one_color("yellow", ansi_yellow());
+ test_one_color("blue", ansi_blue());
+ test_one_color("megenta", ansi_magenta());
+ test_one_color("grey", ansi_grey());
+ test_one_color("highlight-red", ansi_highlight_red());
+ test_one_color("highlight-green", ansi_highlight_green());
+ test_one_color("highlight-yellow", ansi_highlight_yellow());
+ test_one_color("highlight-blue", ansi_highlight_blue());
+ test_one_color("highlight-magenta", ansi_highlight_magenta());
+ test_one_color("highlight-grey", ansi_highlight_grey());
+
+ test_one_color("underline", ansi_underline());
+ test_one_color("highlight-underline", ansi_highlight_underline());
+ test_one_color("highlight-red-underline", ansi_highlight_red_underline());
+ test_one_color("highlight-green-underline", ansi_highlight_green_underline());
+ test_one_color("highlight-yellow-underline", ansi_highlight_yellow_underline());
+ test_one_color("highlight-blue-underline", ansi_highlight_blue_underline());
+ test_one_color("highlight-magenta-underline", ansi_highlight_magenta_underline());
+ test_one_color("highlight-grey-underline", ansi_highlight_grey_underline());
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
test_default_term_for_tty();
test_read_one_char();
test_getttyname_malloc();
+ test_colors();
return 0;
}
--- /dev/null
+/* 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;
+}
#include "util.h"
static void test_utf8_is_printable(void) {
+ log_info("/* %s */", __func__);
+
assert_se(utf8_is_printable("ascii is valid\tunicode", 22));
assert_se(utf8_is_printable("\342\204\242", 3));
assert_se(!utf8_is_printable("\341\204", 2));
}
static void test_utf8_is_valid(void) {
+ log_info("/* %s */", __func__);
+
assert_se(utf8_is_valid("ascii is valid unicode"));
assert_se(utf8_is_valid("\342\204\242"));
assert_se(!utf8_is_valid("\341\204"));
}
static void test_ascii_is_valid(void) {
+ log_info("/* %s */", __func__);
+
assert_se( ascii_is_valid("alsdjf\t\vbarr\nba z"));
assert_se(!ascii_is_valid("\342\204\242"));
assert_se(!ascii_is_valid("\341\204"));
}
static void test_ascii_is_valid_n(void) {
+ log_info("/* %s */", __func__);
+
assert_se( ascii_is_valid_n("alsdjf\t\vbarr\nba z", 17));
assert_se( ascii_is_valid_n("alsdjf\t\vbarr\nba z", 16));
assert_se(!ascii_is_valid_n("alsdjf\t\vbarr\nba z", 18));
}
static void test_utf8_encoded_valid_unichar(void) {
+ log_info("/* %s */", __func__);
+
assert_se(utf8_encoded_valid_unichar("\342\204\242", 1) == -EINVAL); /* truncated */
assert_se(utf8_encoded_valid_unichar("\342\204\242", 2) == -EINVAL); /* truncated */
assert_se(utf8_encoded_valid_unichar("\342\204\242", 3) == 3);
assert_se(utf8_encoded_valid_unichar("\341\204\341\204", 5) == -EINVAL);
}
-static void test_utf8_escaping(void) {
+static void test_utf8_escape_invalid(void) {
_cleanup_free_ char *p1, *p2, *p3;
+ log_info("/* %s */", __func__);
+
p1 = utf8_escape_invalid("goo goo goo");
puts(p1);
assert_se(utf8_is_valid(p1));
assert_se(utf8_is_valid(p3));
}
-static void test_utf8_escaping_printable(void) {
+static void test_utf8_escape_non_printable(void) {
_cleanup_free_ char *p1, *p2, *p3, *p4, *p5, *p6;
+ log_info("/* %s */", __func__);
+
p1 = utf8_escape_non_printable("goo goo goo");
puts(p1);
assert_se(utf8_is_valid(p1));
assert_se(utf8_is_valid(p6));
}
+static void test_utf8_escape_non_printable_full(void) {
+ log_info("/* %s */", __func__);
+
+ for (size_t i = 0; i < 20; i++) {
+ _cleanup_free_ char *p;
+
+ p = utf8_escape_non_printable_full("goo goo goo", i);
+ puts(p);
+ assert_se(utf8_is_valid(p));
+ assert_se(utf8_console_width(p) <= i);
+ }
+
+ for (size_t i = 0; i < 20; i++) {
+ _cleanup_free_ char *p;
+
+ p = utf8_escape_non_printable_full("\001 \019\20\a", i);
+ puts(p);
+ assert_se(utf8_is_valid(p));
+ assert_se(utf8_console_width(p) <= i);
+ }
+
+ for (size_t i = 0; i < 20; i++) {
+ _cleanup_free_ char *p;
+
+ p = utf8_escape_non_printable_full("\xef\xbf\x30\x13", i);
+ puts(p);
+ assert_se(utf8_is_valid(p));
+ assert_se(utf8_console_width(p) <= i);
+ }
+}
+
static void test_utf16_to_utf8(void) {
const char16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
static const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7 };
_cleanup_free_ char16_t *b = NULL;
_cleanup_free_ char *a = NULL;
+ log_info("/* %s */", __func__);
+
/* Convert UTF-16 to UTF-8, filtering embedded bad chars */
a = utf16_to_utf8(utf16, sizeof(utf16));
assert_se(a);
}
static void test_utf8_n_codepoints(void) {
+ log_info("/* %s */", __func__);
+
assert_se(utf8_n_codepoints("abc") == 3);
assert_se(utf8_n_codepoints("zażółcić gęślą jaźń") == 19);
assert_se(utf8_n_codepoints("串") == 1);
}
static void test_utf8_console_width(void) {
+ log_info("/* %s */", __func__);
+
assert_se(utf8_console_width("abc") == 3);
assert_se(utf8_console_width("zażółcić gęślą jaźń") == 19);
assert_se(utf8_console_width("串") == 2);
static void test_utf8_to_utf16(void) {
const char *p;
+ log_info("/* %s */", __func__);
+
FOREACH_STRING(p,
"abc",
"zażółcić gęślą jaźń",
test_ascii_is_valid();
test_ascii_is_valid_n();
test_utf8_encoded_valid_unichar();
- test_utf8_escaping();
- test_utf8_escaping_printable();
+ test_utf8_escape_invalid();
+ test_utf8_escape_non_printable();
+ test_utf8_escape_non_printable_full();
test_utf16_to_utf8();
test_utf8_n_codepoints();
test_utf8_console_width();
--- /dev/null
+/* 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;
+}
int r;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
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();
return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
}
+static mode_t process_mask_perms(mode_t mode, mode_t current) {
+
+ if ((current & 0111) == 0)
+ mode &= ~0111;
+ if ((current & 0222) == 0)
+ mode &= ~0222;
+ if ((current & 0444) == 0)
+ mode &= ~0444;
+ if (!S_ISDIR(current))
+ mode &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
+
+ return mode;
+}
+
static int fd_set_perms(Item *i, int fd, const char *path, const struct stat *st) {
struct stat stbuf;
+ mode_t new_mode;
+ bool do_chown;
assert(i);
assert(fd);
"Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
path);
- if (i->mode_set) {
+ /* Do we need a chown()? */
+ do_chown =
+ (i->uid_set && i->uid != st->st_uid) ||
+ (i->gid_set && i->gid != st->st_gid);
+
+ /* Calculate the mode to apply */
+ new_mode = i->mode_set ? (i->mask_perms ?
+ process_mask_perms(i->mode, st->st_mode) :
+ i->mode) :
+ (st->st_mode & 07777);
+
+ if (i->mode_set && do_chown) {
+ /* Before we issue the chmod() let's reduce the access mode to the common bits of the old and
+ * the new mode. That way there's no time window where the file exists under the old owner
+ * with more than the old access modes — and not under the new owner with more than the new
+ * access modes either. */
+
if (S_ISLNK(st->st_mode))
- log_debug("Skipping mode fix for symlink %s.", path);
+ log_debug("Skipping temporary mode fix for symlink %s.", path);
else {
- mode_t m = i->mode;
-
- if (i->mask_perms) {
- if (!(st->st_mode & 0111))
- m &= ~0111;
- if (!(st->st_mode & 0222))
- m &= ~0222;
- if (!(st->st_mode & 0444))
- m &= ~0444;
- if (!S_ISDIR(st->st_mode))
- m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
- }
+ mode_t m = new_mode & st->st_mode; /* Mask new mode by old mode */
- if (m == (st->st_mode & 07777))
- log_debug("\"%s\" has correct mode %o already.", path, st->st_mode);
+ if (((m ^ st->st_mode) & 07777) == 0)
+ log_debug("\"%s\" matches temporary mode %o already.", path, m);
else {
- log_debug("Changing \"%s\" to mode %o.", path, m);
+ log_debug("Temporarily changing \"%s\" to mode %o.", path, m);
if (fchmod_opath(fd, m) < 0)
return log_error_errno(errno, "fchmod() of %s failed: %m", path);
}
}
}
- if ((i->uid_set && i->uid != st->st_uid) ||
- (i->gid_set && i->gid != st->st_gid)) {
+ if (do_chown) {
log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT,
path,
i->uid_set ? i->uid : UID_INVALID,
return log_error_errno(errno, "fchownat() of %s failed: %m", path);
}
+ /* Now, apply the final mode. We do this in two cases: when the user set a mode explicitly, or after a
+ * chown(), since chown()'s mangle the access mode in regards to sgid/suid in some conditions. */
+ if (i->mode_set || do_chown) {
+ if (S_ISLNK(st->st_mode))
+ log_debug("Skipping mode fix for symlink %s.", path);
+ else {
+ /* Check if the chmod() is unnecessary. Note that if we did a chown() before we always
+ * chmod() here again, since it might have mangled the bits. */
+ if (!do_chown && ((new_mode ^ st->st_mode) & 07777) == 0)
+ log_debug("\"%s\" matches mode %o already.", path, new_mode);
+ else {
+ log_debug("Changing \"%s\" to mode %o.", path, new_mode);
+ if (fchmod_opath(fd, new_mode) < 0)
+ return log_error_errno(errno, "fchmod() of %s failed: %m", path);
+ }
+ }
+ }
+
shortcut:
return label_fix(path, 0);
}
assert(i);
- if (i->argument == NULL)
+ if (!i->argument)
return 0;
switch (i->type) {
}
out:
- if (out_is_packet_device != NULL)
+ if (out_is_packet_device)
*out_is_packet_device = is_packet_device;
return ret;
}
}
node = argv[optind];
- if (node == NULL) {
+ if (!node) {
log_error("no node specified");
return 1;
}
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) {
}
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 {
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;
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;
}
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;
}
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;
}
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;
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");
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;
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)) {
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);
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;
}
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;
};
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;
}
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:
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");
}
}
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;
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;
}
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;
}
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;
}
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);
(void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
- if (name_assign_type == NET_NAME_ENUM) {
+ if (name_assign_type == NET_NAME_ENUM && !strv_contains(link->match_name, "*")) {
log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
link->filename, sysname);
*ret = link;
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;
int retval = 0;
f = fopen(config_file, "re");
- if (f == NULL) {
+ if (!f) {
if (errno == ENOENT)
return 1;
else {
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') {
break;
}
if (vendor == NULL) {
- if (vendor_in == NULL)
+ if (!vendor_in)
break;
} else if (vendor_in &&
startswith(vendor, vendor_in) &&
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':
* 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;
* 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);
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);
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);
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) {
* 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;
}
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;
}
/*
* 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 */
}
}
- 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;
* Examine each descriptor returned. There is normally only
* 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],
+ for (j = 4; j <= ((unsigned)page_83[2] << 8) + (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,
serial, serial_short, len,
wwn, wwn_vendor_extension,
tgpt_group);
* 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;
*
* 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>
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);
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;
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))
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] &&
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),
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "sd-event.h"
return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
if (rename(slink_tmp, slink) < 0) {
- r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s' failed: %m", slink_tmp, slink);
+ r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
(void) unlink(slink_tmp);
}
#include "udev.h"
#include "user-util.h"
+#define WORKER_NUM_MAX 2048U
+
static bool arg_debug = false;
static int arg_daemonize = false;
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
return r;
if (arg_children_max == 0) {
+ unsigned long cpu_limit, mem_limit;
+ unsigned long cpu_count = 1;
cpu_set_t cpu_set;
- unsigned long mem_limit;
-
- arg_children_max = 8;
if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0)
- arg_children_max += CPU_COUNT(&cpu_set) * 8;
+ cpu_count = CPU_COUNT(&cpu_set);
+
+ cpu_limit = cpu_count * 2 + 16;
+ mem_limit = MAX(physical_memory() / (128UL*1024*1024), 10U);
- mem_limit = physical_memory() / (128LU*1024*1024);
- arg_children_max = MAX(10U, MIN(arg_children_max, mem_limit));
+ arg_children_max = MIN(cpu_limit, mem_limit);
+ arg_children_max = MIN(WORKER_NUM_MAX, arg_children_max);
log_debug("Set children_max to %u", arg_children_max);
}
}
device = argv[optind];
- if (device == NULL)
+ if (!device)
return 2;
fd = open(device, O_RDONLY);
return verify_vc_allocation(vcs.v_active);
}
-static int verify_vc_kbmode(int fd) {
- int curr_mode;
-
- /*
- * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
- * Otherwise we would (likely) interfere with X11's processing of the
- * key events.
- *
- * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
- */
-
- if (ioctl(fd, KDGKBMODE, &curr_mode) < 0)
- return -errno;
-
- return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY;
-}
-
static int toggle_utf8(const char *name, int fd, bool utf8) {
int r;
struct termios tc = {};
assert(name);
+ r = vt_verify_kbmode(fd);
+ if (r == -EBUSY) {
+ log_warning_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", name);
+ return 0;
+ } else if (r < 0)
+ return log_warning_errno(r, "Failed to verify kbdmode on %s: %m", name);
+
r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE);
if (r < 0)
return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8), name);
continue;
}
- if (verify_vc_kbmode(fd_d) < 0)
+ if (vt_verify_kbmode(fd_d) < 0)
continue;
toggle_utf8(ttyname, fd_d, utf8);
err = -fd;
continue;
}
- r = verify_vc_kbmode(fd);
+ r = vt_verify_kbmode(fd);
if (r < 0) {
if (!err)
err = -r;
if (r < 0)
return log_error_errno(r, "Virtual console %s is not allocated: %m", src_vc);
- r = verify_vc_kbmode(fd);
+ r = vt_verify_kbmode(fd);
if (r < 0)
return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc);
# Enable regular file and FIFO protection
fs.protected_regular = 1
fs.protected_fifos = 1
-
-# Bump the numeric PID range to its maximum of 2^22 (from the in-kernel default
-# of 2^16), to make PID collisions less likely.
-kernel.pid_max = 4194304
--- /dev/null
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+# See sysctl.d(5) and core(5) for documentation.
+
+# To override settings in this file, create a local file in /etc
+# (e.g. /etc/sysctl.d/90-override.conf), and put any assignments
+# there.
+
+# Bump the numeric PID range to its maximum of 2^22 (from the in-kernel default
+# of 2^16), to make PID collisions less likely.
+kernel.pid_max = 4194304
in_files = []
+# Kernel determines PID_MAX_LIMIT by
+# #define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \
+# (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT))
+if cc.sizeof('long') > 4
+ install_data('50-pid-max.conf', install_dir : sysctldir)
+endif
+
if conf.get('ENABLE_COREDUMP') == 1
in_files += ['50-coredump.conf']
endif
After=multi-user.target
[Service]
-ExecStart=/bin/sh -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; systemctl daemon-reload ; echo OK > /testok'
+ExecStart=/bin/sh -e -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; systemctl daemon-reload ; echo OK > /testok'
Type=oneshot
EOF
return 0
fi
if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]]; then
- printf "Cgroup namespaces are not supported. Skipping.\n" >&2
+ printf "CGroup namespaces are not supported. Skipping.\n" >&2
return 0
fi
--- /dev/null
+#!/bin/bash
+
+set -e
+set -x
+
+# Make sure that the "stat" output is not locale dependent.
+export LANG=C LC_ALL=C
+
+# first, create file without suid/sgid
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0755 1 1 - -
+f /tmp/yyy 0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# then, add suid/sgid
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 04755
+f /tmp/yyy 02755
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
+
+# then, chown the files to somebody else
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx - 2 2
+f /tmp/yyy - 2 2
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
+
+# then, chown the files to a third user/group but also drop to a mask that has
+# both more and fewer bits set
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0770 3 3
+f /tmp/yyy 0770 3 3
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
+
+# return to the beginning
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx 0755 1 1 - -
+f /tmp/yyy 0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# remove everything
+systemd-tmpfiles --remove - <<EOF
+r /tmp/xxx
+r /tmp/yyy
+EOF
#!/bin/bash
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
TEST_DESCRIPTION="test OOM killer logic"
TEST_NO_NSPAWN=1
#!/bin/bash
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
set -o pipefail
Slice=dml-passthrough.slice
Type=oneshot
ExecStart=/bin/true
-MemoryLow=25
+MemoryLow=0
PrivateKey=
PrivateKeyFile=
FwMark=
+FirewallMark=
[MACVTAP]
Mode=
[Match]
UDP6ZeroChecksumRx=
Remote=
UDP6ZeroCheckSumRx=
+IPDoNotFragment=
[Bridge]
ForwardDelaySec=
HelloTimeSec=
FDBAgeingSec=
MacLearning=
Id=
+VNI=
RemoteChecksumRx=
TOS=
L2MissNotification=
UDP6ZeroCheckSumRx=
UDPCheckSum=
GroupPolicyExtension=
+GenericProtocolExtension=
MaximumFDBEntries=
TTL=
DestinationPort=
PortRange=
UDPChecksum=
UDP6ZeroCheckSumTx=
+IPDoNotFragment=
[VXCAN]
Peer=
[Bond]
[FooOverUDP]
Protocol=
Port=
+PeerPort=
Encapsulation=
+Local=
+Peer=
[Tap]
MultiQueue=
OneQueue=
[IPVLAN]
Mode=
Flags=
+[IPVTAP]
+Mode=
+Flags=
[Tun]
OneQueue=
MultiQueue=
PacketInfo=
+VNetHeader=
Group=
User=
[NetDev]
PeerSessionId=
Layer2SpecificHeader=
Name=
-[MACSEC]
+[MACsec]
Port=
Encrypt=
[MACsecReceiveAssociation]
MulticastFlood=
NeighborSuppression=
Learning=
+ProxyARP=
+ProxyARPWiFi=
+MulticastRouter=
[Match]
KernelVersion=
Type=
[BridgeFDB]
VLANId=
MACAddress=
+Destination=
+VNI=
+AssociatedWith=
[DHCP]
UseDomains=
UseRoutes=
ListenPort=
UseTimezone=
RouteTable=
+BlackList=
+SendRelease=
[Route]
Destination=
Protocol=
Scope=
MTUBytes=
QuickAck=
+FastOpenNoCookie=
Source=
Metric=
+TTLPropagate=
[Network]
IPv6DuplicateAddressDetection=
IPMasquerade=
ProxyARP=
PrimarySlave=
IPv4LLRoute=
+DefaultRouteOnDevice=
Address=
IPv6ProxyNDPAddress=
IPv6AcceptRA=
Gateway=
IPv4LL=
IPVLAN=
+IPVTAP=
EmitLLDP=
IPv6MTUBytes=
IPv4ProxyARP=
OtherInformation=
[Neighbor]
Address=
-MacAddress=
+MACAddress=
[IPv6AddressLabel]
Label=
Prefix=
UseDNS=
UseAutonomousPrefix=
UseOnLinkPrefix=
+BlackList=
[DHCPServer]
EmitNTP=
PoolSize=
DefaultLeaseTimeSec=
EmitTimezone=
DNS=
+MaxAttempts=
[DHCPv4]
UseHostname=
UseMTU=
--- /dev/null
+{"method":" "}\0
\ No newline at end of file
--- /dev/null
+ {"method":" "}\0
\ No newline at end of file
--- /dev/null
+ {"method":" "}\0 { "method": " "}\0 { "method": " "}\0 vvvvvvvv\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
\ No newline at end of file
[Service]
ExecStart=/bin/sh -c 'echo "This should not be seen"'
Type=oneshot
+LimitCORE=0
SystemCallFilter=ioperm
SystemCallFilter=~ioperm
SystemCallFilter=ioperm
[Service]
ExecStart=/bin/sh -c 'echo "This should not be seen"'
Type=oneshot
+LimitCORE=0
SystemCallFilter=~write open execve exit_group close mmap munmap fstat DONOTEXIST
TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
[[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
-EFI_MOUNT="$(bootctl -p 2>/dev/null || echo /boot)"
+EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
QEMU_MEM="${QEMU_MEM:-512M}"
if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
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
+
+ # We need to correctly distinguish between gcc's and clang's ASan DSOs.
+ if ldd $BUILD_DIR/systemd | grep -q libasan.so; then
+ ASAN_COMPILER=gcc
+ elif ldd $BUILD_DIR/systemd | grep -q libclang_rt.asan; then
+ ASAN_COMPILER=clang
+
+ # As clang's ASan DSO is usually in a non-standard path, let's check if
+ # the environment is set accordingly. If not, warn the user and exit.
+ # We're not setting the LD_LIBRARY_PATH automagically here, because
+ # user should encounter (and fix) the same issue when running the unit
+ # tests (meson test)
+ if ldd "$BUILD_DIR/systemd" | grep -q "libclang_rt.asan.*not found"; then
+ _asan_rt_name="$(ldd $BUILD_DIR/systemd | awk '/libclang_rt.asan/ {print $1; exit}')"
+ _asan_rt_path="$(find /usr/lib* /usr/local/lib* -type f -name "$_asan_rt_name" 2>/dev/null | sed 1q)"
+ echo >&2 "clang's ASan DSO ($_asan_rt_name) is not present in the runtime library path"
+ echo >&2 "Consider setting LD_LIBRARY_PATH=${_asan_rt_path%/*}"
+ exit 1
+ fi
+ else
+ echo >&2 "systemd is not linked against the ASan DSO"
+ echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
+ exit 1
+ fi
fi
function find_qemu_bin() {
install_depmod_files
generate_module_dependencies
if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
- create_asan_wrapper
+ create_asan_wrapper
fi
}
create_asan_wrapper() {
local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
+ local _asan_rt_pattern
ddebug "Create $_asan_wrapper"
+
+ case "$ASAN_COMPILER" in
+ gcc)
+ _asan_rt_pattern="*libasan*"
+ ;;
+ clang)
+ _asan_rt_pattern="libclang_rt.asan-*"
+ # Install llvm-symbolizer to generate useful reports
+ # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
+ dracut_install "llvm-symbolizer"
+ ;;
+ *)
+ dfail "Unsupported compiler: $ASAN_COMPILER"
+ exit 1
+ esac
+
cat >$_asan_wrapper <<EOF
#!/bin/bash
DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
+# As right now bash is the PID 1, we can't expect PATH to have a sane value.
+# Let's make one to prevent unexpected "<bin> not found" issues in the future
+export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
+
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -o remount,rw /
-PATH_TO_ASAN=\$(find / -name '*libasan*' | sed 1q)
+PATH_TO_ASAN=\$(find / -name '$_asan_rt_pattern' | sed 1q)
if [[ "\$PATH_TO_ASAN" ]]; then
# A lot of services (most notably dbus) won't start without preloading libasan
# See https://github.com/systemd/systemd/issues/5004
DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
+ # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
+ # unnecessary for gcc & libasan, however, for clang this is crucial, as its
+ # runtime ASan DSO is in a non-standard (library) path.
+ echo \${PATH_TO_ASAN%/*} > /etc/ld.so.conf.d/asan-path-override.conf
+ ldconfig
fi
echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
+echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
# ASAN and syscall filters aren't compatible with each other.
find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
# 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.
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
install_missing_libraries() {
# install possible missing libraries
for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
- LD_LIBRARY_PATH=$(get_ldpath $i) inst_libs $i
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
done
}
create_empty_image() {
local _size=500
if [[ "$STRIP_BINARIES" = "no" ]]; then
- _size=$((2*_size))
+ _size=$((4*_size))
fi
rm -f "$TESTDIR/rootdisk.img"
# Create the blank file to use as a root filesystem
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
install_dbus() {
inst $ROOTLIBDIR/system/dbus.socket
- # Fedora rawhide replaced dbus.service with dbus-daemon.service
- if [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ # Newer Fedora versions use dbus-broker by default. Let's install it is available.
+ if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
+ inst $ROOTLIBDIR/system/dbus-broker.service
+ inst_symlink /etc/systemd/system/dbus.service
+ inst /usr/bin/dbus-broker
+ inst /usr/bin/dbus-broker-launch
+ elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ # Fedora rawhide replaced dbus.service with dbus-daemon.service
inst $ROOTLIBDIR/system/dbus-daemon.service
# Alias symlink
inst_symlink /etc/systemd/system/dbus.service
else
inst $ROOTLIBDIR/system/dbus.service
fi
- # Newer Fedora versions use dbus-broker by default. Let's install it is available.
- [ -f /usr/bin/dbus-broker ] && inst /usr/bin/dbus-broker
- [ -f /usr/bin/dbus-broker-launch ] && inst /usr/bin/dbus-broker-launch
find \
/etc/dbus-1 /usr/share/dbus-1 -xtype f \
[Network]
IPv6AcceptRA=false
Address=192.168.23.5/24
+LinkLocalAddressing=yes
[NetDev]
-Name=bond99
+Name=bond98
Kind=bond
[Bond]
[NetDev]
Name=ipvlan99
Kind=ipvlan
-
-[IPVLAN]
-Mode=L2
--- /dev/null
+[NetDev]
+Name=ipvtap99
+Kind=ipvtap
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
+LinkLocalAddressing=yes
--- /dev/null
+[NetDev]
+Kind=netdevsim
+Name=netdevsim99
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
[Tap]
MultiQueue=true
PacketInfo=true
+VNetHeader=true
[Tun]
MultiQueue=true
PacketInfo=true
+VNetHeader=true
--- /dev/null
+[Match]
+Name=*tun98 *tap98 ip6tnl98 erspan98
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::17/64
+Address=10.3.2.4/16
+LinkLocalAddressing=yes
--- /dev/null
+[Match]
+Name=*tun97 ip6tnl97
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::18/64
+Address=10.3.2.5/16
+LinkLocalAddressing=yes
--- /dev/null
+[Match]
+Name=*tun99 *tap99 ip6tnl99 erspan99
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::16/64
+Address=10.3.2.3/16
+LinkLocalAddressing=yes
--- /dev/null
+[NetDev]
+Name=vxcan99
+Kind=vxcan
+
+[VXCAN]
+Peer=vxcan-peer
Kind=vxlan
[VXLAN]
-Id=999
+VNI=999
L2MissNotification=true
L3MissNotification=true
RouteShortCircuit=true
[Network]
DHCP=ipv6
+IPv6Token=::1a:2b:3c:4d
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+LinkLocalAddressing=fallback
+IPv6AcceptRA=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+LinkLocalAddressing=fallback
+IPv6AcceptRA=no
+
+[DHCP]
+MaxAttempts=1
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+IPVTAP=ipvtap99
[Network]
IPv6AcceptRA=no
+LinkLocalAddressing=yes
MACsec=macsec99
--- /dev/null
+[Match]
+Name=ipvlan99
+Name=ipvtap99
+Name=macvlan99
+Name=macvtap99
+Name=veth99
+Name=veth-peer
+Name=vcan99
+Name=vxcan99
+Name=vxcan-peer
+Name=vrf99
+Name=geneve99
+Name=ipiptun99
+
+[Network]
+LinkLocalAddressing=yes
+IPv6AcceptRA=no
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=false
+LinkLocalAddressing=yes
+VXLAN=vxlan99
[Match]
-Name=test1
+Name=vxlan99
[Network]
-VXLAN=vxlan99
+IPv6AcceptRA=no
+LinkLocalAddressing=yes
+
+[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
with open(os.path.join(path, attribute)) as f:
return f.readline().strip()
- def link_exits(self, link):
+ def link_exists(self, link):
return os.path.exists(os.path.join('/sys/class/net', link))
+ def check_link_exists(self, link):
+ self.assertTrue(self.link_exists(link))
+
def link_remove(self, links):
for link in links:
- if os.path.exists(os.path.join('/sys/class/net', link)):
+ if self.link_exists(link):
subprocess.call(['ip', 'link', 'del', 'dev', link])
time.sleep(1)
def l2tp_tunnel_remove(self, tunnel_ids):
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel'], universal_newlines=True).rstrip()
for tid in tunnel_ids:
words='Tunnel ' + tid + ', encap'
if words in output:
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)
contents = in_file.read()
if show_all:
print(contents)
- for line in contents.split('\n'):
+ for line in contents.splitlines():
if words in line:
in_file.close()
print("%s, %s" % (words, line))
args = [wait_online_bin, f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
if bool_any:
args += ['--any']
- subprocess.check_call(args)
+ try:
+ subprocess.check_call(args)
+ except subprocess.CalledProcessError:
+ for link in links_with_operstate:
+ output = subprocess.check_output(['networkctl', 'status', link.split(':')[0]], universal_newlines=True).rstrip()
+ print(output)
+ raise
+
+ def get_operstate(self, link, show_status=True, setup_state='configured'):
+ output = subprocess.check_output(['networkctl', 'status', link], universal_newlines=True).rstrip()
+ if show_status:
+ print(output)
+ for line in output.splitlines():
+ if 'State:' in line and (not setup_state or setup_state in line):
+ return line.split()[1]
+ return None
+
+ def check_operstate(self, link, expected, show_status=True, setup_state='configured'):
+ self.assertRegex(self.get_operstate(link, show_status, setup_state), expected)
+
class NetworkdNetDevTests(unittest.TestCase, Utilities):
'ipiptun98',
'ipiptun99',
'ipvlan99',
+ 'ipvtap99',
'isataptun99',
'macvlan99',
'macvtap99',
'vtitun97',
'vtitun98',
'vtitun99',
+ 'vxcan99',
'vxlan99',
'wg98',
'wg99']
'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',
'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',
'25-vti-tunnel-local-any.netdev',
'25-vti-tunnel-remote-any.netdev',
'25-vti-tunnel.netdev',
+ '25-vxcan.netdev',
'25-vxlan.netdev',
'25-wireguard-23-peers.netdev',
'25-wireguard-23-peers.network',
'ip6tnl.network',
'ipip.network',
'ipvlan.network',
+ 'ipvtap.network',
'isatap.network',
'macsec.network',
'macvlan.network',
'macvtap.network',
+ 'netdev-link-local-addressing-yes.network',
'sit.network',
'vti6.network',
'vti.network',
+ 'vxlan-test1.network',
'vxlan.network']
def setUp(self):
self.link_remove(self.links)
self.remove_unit_from_networkd_path(self.units)
- def test_dropin(self):
+ def test_dropin_and_networkctl_glob(self):
self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('dropin-test'))
+ self.wait_online(['dropin-test:off'])
# This also tests NetDev.Name= conflict and basic networkctl functionalities
- output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '00:50:56:c0:00:28')
- output = subprocess.check_output(['networkctl', 'list']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'list'], universal_newlines=True).rstrip()
self.assertRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
- output = subprocess.check_output(['networkctl', 'list', 'dropin-test']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'list', 'dropin-test'], universal_newlines=True).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
- output = subprocess.check_output(['networkctl', 'list', 'dropin-*']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'list', 'dropin-*'], universal_newlines=True).rstrip()
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'dropin-test')
- output = subprocess.check_output(['networkctl', 'status', 'dropin-*']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'dropin-*'], universal_newlines=True).rstrip()
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'dropin-test')
- ret = subprocess.run(['ethtool', '--driver', 'dropin-test'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- print(ret.stdout.rstrip().decode('utf-8'))
- if ret.returncode == 0 and re.search('driver: dummy', ret.stdout.rstrip().decode('utf-8')) != None:
+ ret = subprocess.run(['ethtool', '--driver', 'dropin-test'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
+ print(ret.stdout.rstrip())
+ if ret.returncode == 0 and re.search('driver: dummy', ret.stdout.rstrip()) != None:
self.assertRegex(output, 'Driver: dummy')
else:
print('ethtool does not support driver field at least for dummy interfaces, skipping test for Driver field of networkctl.')
self.start_networkd(0)
self.wait_online(['bridge99', 'test1:degraded'], bool_any=True)
- self.assertTrue(self.link_exits('bridge99'))
- self.assertTrue(self.link_exits('test1'))
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: (?:off|no-carrier) \(configuring\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: degraded \(configured\)')
+ self.check_operstate('bridge99', '(?:off|no-carrier)', setup_state='configuring')
+ self.check_operstate('test1', 'degraded')
def test_bridge(self):
self.copy_unit_to_networkd_unit_path('25-bridge.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('bridge99'))
+ self.wait_online(['bridge99:off'])
- self.assertEqual('900', self.read_link_attr('bridge99', 'bridge', 'hello_time'))
- self.assertEqual('900', self.read_link_attr('bridge99', 'bridge', 'max_age'))
- self.assertEqual('900', self.read_link_attr('bridge99', 'bridge','forward_delay'))
- self.assertEqual('900', self.read_link_attr('bridge99', 'bridge','ageing_time'))
- self.assertEqual('9', self.read_link_attr('bridge99', 'bridge','priority'))
- self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','multicast_querier'))
- self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','multicast_snooping'))
- self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','stp_state'))
+ tick = os.sysconf('SC_CLK_TCK')
+ self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
+ self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
+ self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge','forward_delay')) / tick))
+ self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge','ageing_time')) / tick))
+ self.assertEqual(9, int(self.read_link_attr('bridge99', 'bridge','priority')))
+ self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','multicast_querier')))
+ self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','multicast_snooping')))
+ self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','stp_state')))
def test_bond(self):
- self.copy_unit_to_networkd_unit_path('25-bond.netdev')
- self.start_networkd()
+ self.copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('bond99'))
+ self.wait_online(['bond99:off', 'bond98:off'])
self.assertEqual('802.3ad 4', self.read_link_attr('bond99', 'bonding', 'mode'))
self.assertEqual('layer3+4 1', self.read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
self.assertEqual('811', self.read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
self.assertEqual('00:11:22:33:44:55', self.read_link_attr('bond99', 'bonding', 'ad_actor_system'))
- def test_bond_balanced_tlb(self):
- self.copy_unit_to_networkd_unit_path('25-bond-balanced-tlb.netdev')
- self.start_networkd()
-
- self.assertTrue(self.link_exits('bond99'))
-
- self.assertEqual('balance-tlb 5', self.read_link_attr('bond99', 'bonding', 'mode'))
- self.assertEqual('1', self.read_link_attr('bond99', 'bonding', 'tlb_dynamic_lb'))
+ self.assertEqual('balance-tlb 5', self.read_link_attr('bond98', 'bonding', 'mode'))
+ self.assertEqual('1', self.read_link_attr('bond98', 'bonding', 'tlb_dynamic_lb'))
def test_vlan(self):
self.copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev',
'21-vlan.network', '21-vlan-test1.network')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('vlan99'))
+ self.wait_online(['test1:degraded', 'vlan99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
- self.assertRegex(output, ' mtu 2004 ')
+ self.assertRegex(output, ' mtu 2000 ')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'REORDER_HDR')
self.assertRegex(output, 'MVRP')
self.assertRegex(output, ' id 99 ')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'vlan99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'vlan99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
def test_macvtap(self):
- self.copy_unit_to_networkd_unit_path('21-macvtap.netdev', '11-dummy.netdev', 'macvtap.network')
- self.start_networkd()
-
- self.assertTrue(self.link_exits('macvtap99'))
+ for mode in ['private', 'vepa', 'bridge', 'passthru']:
+ with self.subTest(mode=mode):
+ if mode != 'private':
+ self.tearDown()
+ self.copy_unit_to_networkd_unit_path('21-macvtap.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'macvtap.network')
+ with open(os.path.join(network_unit_file_path, '21-macvtap.netdev'), mode='a') as f:
+ f.write('[MACVTAP]\nMode=' + mode)
+ self.start_networkd(0)
+
+ self.wait_online(['macvtap99:degraded', 'test1:degraded'])
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvtap99'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, 'macvtap mode ' + mode + ' ')
def test_macvlan(self):
- self.copy_unit_to_networkd_unit_path('21-macvlan.netdev', '11-dummy.netdev', 'macvlan.network')
- self.start_networkd()
-
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('macvlan99'))
-
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, ' mtu 2000 ')
-
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvlan99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, ' mtu 2000 ')
+ for mode in ['private', 'vepa', 'bridge', 'passthru']:
+ with self.subTest(mode=mode):
+ if mode != 'private':
+ self.tearDown()
+ self.copy_unit_to_networkd_unit_path('21-macvlan.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'macvlan.network')
+ with open(os.path.join(network_unit_file_path, '21-macvlan.netdev'), mode='a') as f:
+ f.write('[MACVLAN]\nMode=' + mode)
+ self.start_networkd(0)
+
+ self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, ' mtu 2000 ')
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvlan99'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, ' mtu 2000 ')
+ self.assertRegex(output, 'macvlan mode ' + mode + ' ')
@expectedFailureIfModuleIsNotAvailable('ipvlan')
def test_ipvlan(self):
- self.copy_unit_to_networkd_unit_path('25-ipvlan.netdev', '11-dummy.netdev', 'ipvlan.network')
- self.start_networkd()
-
- self.assertTrue(self.link_exits('ipvlan99'))
+ for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
+ with self.subTest(mode=mode, flag=flag):
+ if mode != 'L2':
+ self.tearDown()
+ self.copy_unit_to_networkd_unit_path('25-ipvlan.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'ipvlan.network')
+ with open(os.path.join(network_unit_file_path, '25-ipvlan.netdev'), mode='a') as f:
+ f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
+
+ self.start_networkd(0)
+ self.wait_online(['ipvlan99:degraded', 'test1:degraded'])
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipvlan99'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
+
+ @expectedFailureIfModuleIsNotAvailable('ipvtap')
+ def test_ipvtap(self):
+ for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
+ with self.subTest(mode=mode, flag=flag):
+ if mode != 'L2':
+ self.tearDown()
+ self.copy_unit_to_networkd_unit_path('25-ipvtap.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'ipvtap.network')
+ with open(os.path.join(network_unit_file_path, '25-ipvtap.netdev'), mode='a') as f:
+ f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
+
+ self.start_networkd(0)
+ self.wait_online(['ipvtap99:degraded', 'test1:degraded'])
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipvtap99'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
def test_veth(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev')
- self.start_networkd()
-
- self.assertTrue(self.link_exits('veth99'))
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
- def test_dummy(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev')
- self.start_networkd()
+ self.wait_online(['veth99:degraded', 'veth-peer:degraded'])
- self.assertTrue(self.link_exits('test1'))
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth99'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth-peer'], universal_newlines=True).rstrip()
+ print(output)
+ self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
def test_tun(self):
self.copy_unit_to_networkd_unit_path('25-tun.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('tun99'))
+ self.wait_online(['tun99:off'])
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'tun99'], universal_newlines=True).rstrip()
+ print(output)
+ # Old ip command does not support IFF_ flags
+ self.assertRegex(output, 'tun (?:type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
def test_tap(self):
self.copy_unit_to_networkd_unit_path('25-tap.netdev')
- self.start_networkd()
+ self.start_networkd(0)
+
+ self.wait_online(['tap99:off'])
- self.assertTrue(self.link_exits('tap99'))
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'tap99'], universal_newlines=True).rstrip()
+ print(output)
+ # Old ip command does not support IFF_ flags
+ self.assertRegex(output, 'tun (?:type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_vrf(self):
- self.copy_unit_to_networkd_unit_path('25-vrf.netdev')
- self.start_networkd()
+ self.copy_unit_to_networkd_unit_path('25-vrf.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('vrf99'))
+ self.wait_online(['vrf99:carrier'])
@expectedFailureIfModuleIsNotAvailable('vcan')
def test_vcan(self):
- self.copy_unit_to_networkd_unit_path('25-vcan.netdev')
- self.start_networkd()
+ self.copy_unit_to_networkd_unit_path('25-vcan.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
+
+ self.wait_online(['vcan99:carrier'])
- self.assertTrue(self.link_exits('vcan99'))
+ @expectedFailureIfModuleIsNotAvailable('vxcan')
+ def test_vxcan(self):
+ self.copy_unit_to_networkd_unit_path('25-vxcan.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
+
+ self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
@expectedFailureIfModuleIsNotAvailable('wireguard')
def test_wireguard(self):
self.start_networkd(0)
self.wait_online(['wg99:carrier', 'wg98:routable'])
- self.assertTrue(self.link_exits('wg99'))
- self.assertTrue(self.link_exits('wg98'))
-
if shutil.which('wg'):
subprocess.call('wg')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'listen-port']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'listen-port'], universal_newlines=True).rstrip()
self.assertRegex(output, '51820')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'fwmark']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'fwmark'], universal_newlines=True).rstrip()
self.assertRegex(output, '0x4d2')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'allowed-ips']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'allowed-ips'], universal_newlines=True).rstrip()
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')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'persistent-keepalive'], universal_newlines=True).rstrip()
self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints'], universal_newlines=True).rstrip()
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')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key'], universal_newlines=True).rstrip()
self.assertRegex(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys'], universal_newlines=True).rstrip()
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')
+ output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key'], universal_newlines=True).rstrip()
self.assertRegex(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=')
def test_geneve(self):
- self.copy_unit_to_networkd_unit_path('25-geneve.netdev')
- self.start_networkd()
+ self.copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('geneve99'))
+ self.wait_online(['geneve99:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'geneve99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'geneve99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.22.1')
self.assertRegex(output, '6082')
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local 192.168.223.238 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun97'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 1.2.3.103')
self.assertRegex(output, 'okey 1.2.4.103')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.104')
self.assertRegex(output, 'okey 0.0.0.104')
self.assertNotRegex(output, 'iseq')
self.assertNotRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun97'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.105')
self.assertNotRegex(output, 'oseq')
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'))
- self.assertTrue(self.link_exits('ip6gretun99'))
- self.assertTrue(self.link_exits('ip6gretun98'))
- self.assertTrue(self.link_exits('ip6gretun97'))
+ # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun99']).rstrip().decode('utf-8')
+ self.check_link_exists('dummy98')
+ self.check_link_exists('ip6gretun99')
+ self.check_link_exists('ip6gretun98')
+ self.check_link_exists('ip6gretun97')
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun97'], universal_newlines=True).rstrip()
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()
-
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('gretap99'))
- self.assertTrue(self.link_exits('gretap98'))
+ 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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.106')
self.assertRegex(output, 'okey 0.0.0.106')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.107')
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap98'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun97'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun97'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl97'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local 10.65.223.238 dev dummy98")
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local any dev dummy98")
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun97']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun97'], universal_newlines=True).rstrip()
print(output)
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.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('isataptun99'))
+ 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'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'isataptun99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'isataptun99'], universal_newlines=True).rstrip()
print(output)
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '6rd-prefix 2602::/24')
@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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'erspan remote 172.16.1.100 local 172.16.1.200')
self.assertRegex(output, 'ikey 0.0.0.101')
self.assertRegex(output, 'okey 0.0.0.101')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'erspan remote 172.16.1.100 local any')
self.assertRegex(output, '102')
self.assertRegex(output, 'oseq')
def test_tunnel_independent(self):
- self.copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev')
- self.start_networkd()
+ self.copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev', 'netdev-link-local-addressing-yes.network')
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('ipiptun99'))
+ self.wait_online(['ipiptun99:carrier'])
@expectedFailureIfModuleIsNotAvailable('fou')
def test_fou(self):
self.copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
'25-fou-ipip.netdev', '25-fou-sit.netdev',
'25-fou-gre.netdev', '25-fou-gretap.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('ipiptun96'))
- self.assertTrue(self.link_exits('sittun96'))
- self.assertTrue(self.link_exits('gretun96'))
- self.assertTrue(self.link_exits('gretap96'))
+ self.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'])
- output = subprocess.check_output(['ip', 'fou', 'show']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'fou', 'show'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'port 55555 ipproto 4')
self.assertRegex(output, 'port 55556 ipproto 47')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun96']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun96'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun96']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun96'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun96']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun96'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap96']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap96'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
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')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vxlan99'], universal_newlines=True).rstrip()
print(output)
- self.assertRegex(output, "999")
+ self.assertRegex(output, '999')
self.assertRegex(output, '5555')
self.assertRegex(output, 'l2miss')
self.assertRegex(output, 'l3miss')
self.assertRegex(output, 'remcsumrx')
self.assertRegex(output, 'gbp')
+ output = subprocess.check_output(['bridge', 'fdb', 'show', 'dev', 'vxlan99'], universal_newlines=True).rstrip()
+ 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')
self.wait_online(['dummy98:degraded', 'macsec99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macsec99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macsec99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'macsec99@dummy98')
self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
self.assertRegex(output, 'encrypt on')
- output = subprocess.check_output(['ip', 'macsec', 'show', 'macsec99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'macsec', 'show', 'macsec99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'encrypt on')
self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
@expectedFailureIfModuleIsNotAvailable('l2tp_eth')
def test_l2tp_udp(self):
self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-udp.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('l2tp-ses1'))
- self.assertTrue(self.link_exits('l2tp-ses2'))
+ self.wait_online(['test1:routable', 'l2tp-ses1:off', 'l2tp-ses2:off'])
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Tunnel 10, encap UDP")
self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
self.assertRegex(output, "UDP source / dest ports: 3000/4000")
self.assertRegex(output, "UDP checksum: enabled")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '15']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '15'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Session 15 in tunnel 10")
self.assertRegex(output, "Peer session 16, tunnel 11")
self.assertRegex(output, "interface name: l2tp-ses1")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '17']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '17'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Session 17 in tunnel 10")
self.assertRegex(output, "Peer session 18, tunnel 11")
@expectedFailureIfModuleIsNotAvailable('l2tp_ip')
def test_l2tp_ip(self):
self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-ip.netdev')
- self.start_networkd()
+ self.start_networkd(0)
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('l2tp-ses3'))
- self.assertTrue(self.link_exits('l2tp-ses4'))
+ self.wait_online(['test1:routable', 'l2tp-ses3:off', 'l2tp-ses4:off'])
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Tunnel 10, encap IP")
self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
self.assertRegex(output, "Peer tunnel 12")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '25']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '25'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Session 25 in tunnel 10")
self.assertRegex(output, "Peer session 26, tunnel 12")
self.assertRegex(output, "interface name: l2tp-ses3")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '27']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '27'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, "Session 27 in tunnel 10")
self.assertRegex(output, "Peer session 28, tunnel 12")
self.wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
self.assertNotRegex(output, '10.10.0.1/16')
self.assertNotRegex(output, '10.10.0.2/16')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '32']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '32'], universal_newlines=True).rstrip()
self.assertRegex(output, 'inet 10.3.2.3/16 brd 10.3.255.255 scope global 32')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '33']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '33'], universal_newlines=True).rstrip()
self.assertRegex(output, 'inet 10.4.2.3 peer 10.4.2.4/16 scope global 33')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '34']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '34'], universal_newlines=True).rstrip()
self.assertRegex(output, 'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255 scope global 34')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '35']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '35'], universal_newlines=True).rstrip()
self.assertRegex(output, 'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255 scope global 35')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet6 2001:db8:0:f101::15/64 scope global')
self.assertRegex(output, 'inet6 2001:db8:0:f101::16/64 scope global')
self.copy_unit_to_networkd_unit_path('25-address-preferred-lifetime-zero-ipv6.network', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: routable \(configuring\)')
+ self.check_operstate('dummy98', 'routable', setup_state='configuring')
- output = subprocess.check_output(['ip', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope link deprecated dummy98')
self.assertRegex(output, 'inet6 2001:db8:0:f101::1/64 scope global')
self.copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('test1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.0.15')
self.assertRegex(output, '192.168.0.1')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('test1')
- output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'from 192.168.100.18')
# Remove state files only first time
self.start_networkd(remove_state_files=(trial == 0))
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('test1')
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '7']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule', 'list', 'table', '7'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '111: from 192.168.100.18 tos (?:0x08|throughput) iif test1 oif test1 lookup 7')
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '8']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule', 'list', 'table', '8'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '112: from 192.168.101.18 tos (?:0x08|throughput) iif dummy98 oif dummy98 lookup 8')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('test1')
- output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'from 192.168.100.18')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('test1')
- output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'not.*?from.*?192.168.100.18')
self.wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
- output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98', 'default'], universal_newlines=True).rstrip()
self.assertRegex(output, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium')
- output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
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')
+ output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98', 'default'], universal_newlines=True).rstrip()
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')
+ output = subprocess.check_output(['ip', 'route', 'show', 'type', 'blackhole'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'blackhole 202.54.1.2 proto static')
- output = subprocess.check_output(['ip', 'route', 'show', 'type', 'unreachable']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'type', 'unreachable'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'unreachable 202.54.1.3 proto static')
- output = subprocess.check_output(['ip', 'route', 'show', 'type', 'prohibit']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'type', 'prohibit'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'prohibit 202.54.1.4 proto static')
self.copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('bond199'))
+ self.check_link_exists('dummy98')
+ self.check_link_exists('bond199')
- output = subprocess.check_output(['ip', '-6', 'route', 'list', 'dev', 'bond199']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'route', 'list', 'dev', 'bond199'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'abcd::/16')
self.assertRegex(output, 'src')
self.copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['ip', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '00:01:02:aa:bb:cc')
self.copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'unmanaged')
self.copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['ip', 'addrlabel', 'list']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'addrlabel', 'list'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '2004:da8:1::/64')
self.copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['ip', 'neigh', 'list']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'neigh', 'list'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
self.start_networkd(0)
self.wait_online(['test1:degraded', 'dummy98:carrier'])
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('test1')
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet .* scope link')
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertNotRegex(output, 'inet6* .* scope link')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: degraded \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: carrier \(configured\)')
+ self.check_operstate('test1', 'degraded')
+ self.check_operstate('dummy98', 'carrier')
'''
Documentation/networking/ip-sysctl.txt
self.start_networkd(0)
self.wait_online(['dummy98:degraded'])
- self.assertTrue(self.link_exits('dummy98'))
-
self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
self.start_networkd(0)
self.wait_online(['dummy98:routable'])
- self.assertTrue(self.link_exits('dummy98'))
-
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertEqual(output, '')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('dummy98', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
self.start_networkd(0)
self.wait_online(['dummy98:routable'])
- self.assertTrue(self.link_exits('dummy98'))
-
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('dummy98', 'routable')
def test_bind_carrier(self):
self.copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('test1')
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('test1', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy99', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy99', 'up']), 0)
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('test1', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('test1', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy99']), 0)
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertNotRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'DOWN')
self.assertNotRegex(output, '192.168.10')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: off \(configured\)')
+ self.check_operstate('test1', 'off')
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('test1', 'routable')
class NetworkdNetWorkBondTests(unittest.TestCase, Utilities):
links = [
self.copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('bond199'))
+ self.check_link_exists('dummy98')
+ self.check_link_exists('bond199')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'active_slave dummy98')
self.copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-test1-bond199.network', '25-bond-active-backup-slave.netdev', '11-dummy.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('bond199'))
+ self.check_link_exists('test1')
+ self.check_link_exists('bond199')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'primary test1')
'bond99.network','bond-slave.network')
self.start_networkd()
- self.assertTrue(self.link_exits('bond99'))
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('test1'))
+ self.check_link_exists('bond99')
+ self.check_link_exists('dummy98')
+ self.check_link_exists('test1')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'MASTER,UP,LOWER_UP')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('dummy98', 'enslaved')
+ self.check_operstate('test1', 'enslaved')
+ self.check_operstate('bond99', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0)
time.sleep(2)
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: off \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: degraded-carrier \(configured\)')
+ self.check_operstate('dummy98', 'off')
+ self.check_operstate('test1', 'enslaved')
+ self.check_operstate('bond99', 'degraded-carrier')
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
time.sleep(2)
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('dummy98', 'enslaved')
+ self.check_operstate('test1', 'enslaved')
+ self.check_operstate('bond99', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'test1', 'down']), 0)
- time.sleep(5)
+ time.sleep(2)
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: off \(configured\)')
+ self.check_operstate('dummy98', 'off')
+ self.check_operstate('test1', 'off')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: off \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: no-carrier \(configured\)')
+ for trial in range(30):
+ if trial > 0:
+ time.sleep(1)
+ output = subprocess.check_output(['ip', 'address', 'show', 'bond99'], universal_newlines=True).rstrip()
+ print(output)
+ if self.get_operstate('bond99') == 'no-carrier':
+ break
+ else:
+ # Huh? Kernel does not recognize that all slave interfaces are down?
+ # Let's confirm that networkd's operstate is consistent with ip's result.
+ self.assertNotRegex(output, 'NO-CARRIER')
class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
links = [
'bridge99.network')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('bridge99'))
+ self.check_link_exists('dummy98')
+ self.check_link_exists('test1')
+ self.check_link_exists('bridge99')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'master')
self.assertRegex(output, 'bridge')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'master')
self.assertRegex(output, 'bridge')
- output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.0.15/24')
- output = subprocess.check_output(['bridge', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['bridge', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
- output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('test1', 'enslaved')
+ self.check_operstate('dummy98', 'enslaved')
+ self.check_operstate('bridge99', 'routable')
self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0)
time.sleep(1)
- output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.0.16/24')
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('bridge99', 'routable')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0)
time.sleep(3)
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: degraded-carrier \(configured\)')
+ self.check_operstate('bridge99', 'degraded-carrier')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
time.sleep(3)
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: no-carrier \(configured\)')
+ self.check_operstate('bridge99', 'no-carrier')
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'NO-CARRIER')
self.assertNotRegex(output, '192.168.0.15/24')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
- self.assertTrue(self.link_exits('test1'))
- self.assertTrue(self.link_exits('bridge99'))
+ self.check_link_exists('dummy98')
+ self.check_link_exists('test1')
+ self.check_link_exists('bridge99')
self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0)
time.sleep(1)
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
time.sleep(3)
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'NO-CARRIER')
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
self.start_networkd()
- self.assertTrue(self.link_exits('bridge99'))
+ self.check_link_exists('bridge99')
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
- time.sleep(3)
+ for trial in range(30):
+ if trial > 0:
+ time.sleep(1)
+ if self.get_operstate('bridge99') == 'routable' and self.get_operstate('dummy98') == 'enslaved':
+ break
+ else:
+ self.assertTrue(False)
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
- output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: routable \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
- self.assertRegex(output, 'State: enslaved \(configured\)')
-
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '100']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'rule', 'list', 'table', '100'], universal_newlines=True).rstrip()
print(output)
self.assertEqual(output, '0: from all to 8.8.8.8 lookup 100')
self.copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
- output = subprocess.check_output(['networkctl', 'lldp']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'lldp'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'veth-peer')
self.assertRegex(output, 'veth99')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '2002:da8:1:0')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5.*')
self.assertRegex(output, 'Gateway: 192.168.5.1')
self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
self.start_networkd()
- self.assertTrue(self.link_exits('dummy98'))
+ self.check_link_exists('dummy98')
- output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'dummy98'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'Address: 192.168.42.100')
self.assertRegex(output, 'DNS: 192.168.42.1')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'Gateway: 192.168.5.*')
self.assertRegex(output, '192.168.5.*')
'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',
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')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
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'], universal_newlines=True).rstrip()
+ 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()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertNotRegex(output, '2600::')
self.assertRegex(output, '192.168.5')
'dhcp-client-ipv4-only.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '2600::')
self.assertRegex(output, '192.168.5')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
print('## ip address show dev veth99')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
self.assertRegex(output, '192.168.5')
# issue #8726
print('## ip route show table main dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertNotRegex(output, 'proto dhcp')
print('## ip route show table 211 dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', '211', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'table', '211', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'default via 192.168.5.1 proto dhcp')
self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 proto dhcp')
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.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
self.assertTrue(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
self.assertFalse(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq('--dhcp-alternate-port=67,5555')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5.* dynamic')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['ip', 'route', 'show', 'table', '12']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'table', '12'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'veth99 proto dhcp')
self.assertRegex(output, '192.168.5.1')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['ip', 'route', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'metric 24')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-critical-connection.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5.*')
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
time.sleep(125)
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5.*')
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99', 'scope', 'global']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5')
self.assertRegex(output, '2600::')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5')
self.assertRegex(output, 'valid_lft forever preferred_lft forever')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '2600::')
self.assertRegex(output, 'valid_lft forever preferred_lft forever')
'25-vrf.netdev', '25-vrf.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
- self.assertTrue(self.link_exits('vrf99'))
+ self.check_link_exists('veth99')
+ self.check_link_exists('vrf99')
self.start_dnsmasq()
print('## ip -d link show dev vrf99')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dev', 'vrf99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dev', 'vrf99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'vrf table 42')
print('## ip address show vrf vrf99')
- output_ip_vrf = subprocess.check_output(['ip', 'address', 'show', 'vrf', 'vrf99']).rstrip().decode('utf-8')
+ output_ip_vrf = subprocess.check_output(['ip', 'address', 'show', 'vrf', 'vrf99'], universal_newlines=True).rstrip()
print(output_ip_vrf)
print('## ip address show dev veth99')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertEqual(output, output_ip_vrf)
self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
self.assertRegex(output, 'inet6 .* scope link')
print('## ip route show vrf vrf99')
- output = subprocess.check_output(['ip', 'route', 'show', 'vrf', 'vrf99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'vrf', 'vrf99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
self.assertRegex(output, 'default dev veth99 proto static scope link')
self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
print('## ip route show table main dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertEqual(output, '')
- output = subprocess.check_output(['networkctl', 'status', 'vrf99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: carrier \(configured\)')
-
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
- print(output)
- self.assertRegex(output, 'State: routable \(configured\)')
+ self.check_operstate('vrf99', 'carrier')
+ self.check_operstate('veth99', 'routable')
def test_dhcp_client_gateway_onlink_implicit(self):
self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
'dhcp-client-gateway-onlink-implicit.network')
self.start_networkd()
- self.assertTrue(self.link_exits('veth99'))
+ self.check_link_exists('veth99')
self.start_dnsmasq()
- output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, '192.168.5')
- output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '10.0.0.0/8']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '10.0.0.0/8'], universal_newlines=True).rstrip()
print(output)
self.assertRegex(output, 'onlink')
- output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '192.168.100.0/24']).rstrip().decode('utf-8')
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '192.168.100.0/24'], universal_newlines=True).rstrip()
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'], universal_newlines=True).rstrip()
+ print(output)
+
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ self.assertRegex(output, 'inet6 .* scope link')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ 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'], universal_newlines=True).rstrip()
+ 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'], universal_newlines=True).rstrip()
+ print(output)
+
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ self.assertRegex(output, 'inet6 .* scope link')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ 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'], universal_newlines=True).rstrip()
+ 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'], universal_newlines=True).rstrip()
+ print(output)
+
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
+ output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ self.assertRegex(output, 'inet6 .* scope link')
+ output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ 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'], universal_newlines=True).rstrip()
+ self.assertRegex(output, 'inet .* scope link')
+
if __name__ == '__main__':
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
verbosity=3))
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'],
#!/bin/bash
set -e
+which perl &>/dev/null || exit 77
+
function generate_directives() {
perl -aF'/[\s,]+/' -ne '
if (my ($s, $d) = ($F[0] =~ /^([^\s\.]+)\.([^\s\.]+)$/)) { $d{$s}{"$d="} = 1; }
}
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 "$1"/src/network/networkd-network-gperf.gperf | sort) \
+ <(cat "$1"/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 "$1"/src/network/netdev/netdev-gperf.gperf | sort) \
+ <(cat "$1"/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 "$1"/src/udev/net/link-config-gperf.gperf | sort) \
+ <(cat "$1"/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
# Apparently git describe has a bug where it always considers the work-tree
# dirty when invoked with --git-dir (even though 'git status' is happy). Work
# around this issue by cd-ing to the source directory.
-cd "$dir" && git describe --abbrev=7 --dirty=+ 2>/dev/null | sed 's/^v//' || echo "$fallback"
+cd "$dir"
+# Check that we have either .git/ (a normal clone) or a .git file (a work-tree)
+# and that we don't get confused if a tarball is extracted in a higher-level
+# git repository.
+[ -e .git ] && git describe --abbrev=7 --dirty=+ 2>/dev/null | sed 's/^v//' || echo "$fallback"
clang_version="$($CC --version | sed -nr 's/.*version ([^ ]+?) .*/\1/p' | sed -r 's/-$//')"
SANITIZER=${SANITIZER:-address -fsanitize-address-use-after-scope}
-flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize-coverage=trace-pc-guard,trace-cmp"
+flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER"
clang_lib="/usr/lib64/clang/${clang_version}/lib/linux"
[ -d "$clang_lib" ] || clang_lib="/usr/lib/clang/${clang_version}/lib/linux"
fi
meson $build -D$fuzzflag -Db_lundef=false
-ninja -C $build fuzzers
+ninja -v -C $build fuzzers
# The seed corpus is a separate flat archive for each fuzzer,
# with a fixed name ${fuzzer}_seed_corpus.zip.
DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
DOCKER_RUN="${DOCKER_RUN:-docker run}"
REPO_ROOT="${REPO_ROOT:-$PWD}"
-ADDITIONAL_DEPS=(python3-libevdev python3-pyparsing clang)
+ADDITIONAL_DEPS=(python3-libevdev
+ python3-pyparsing
+ clang
+ perl)
function info() {
echo -e "\033[33;1m$1\033[0m"
if [[ "$phase" = "RUN_CLANG" ]]; then
ENV_VARS="-e CC=clang -e CXX=clang++"
fi
- docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true build
+ docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true -Dman=true build
$DOCKER_EXEC ninja -v -C build
docker exec -e "TRAVIS=$TRAVIS" -it $CONT_NAME ninja -C build test
- $DOCKER_EXEC tools/check-directives.sh
;;
RUN_ASAN|RUN_CLANG_ASAN)
if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
DOCKER_RUN="${DOCKER_RUN:-docker run}"
REPO_ROOT="${REPO_ROOT:-$PWD}"
-ADDITIONAL_DEPS=(dnf-plugins-core python2 iputils hostname libasan python3-pyparsing python3-evdev libubsan clang llvm)
+ADDITIONAL_DEPS=(dnf-plugins-core
+ python2 iputils
+ hostname libasan
+ python3-pyparsing
+ python3-evdev
+ libubsan
+ clang
+ llvm
+ perl)
function info() {
echo -e "\033[33;1m$1\033[0m"
$DOCKER_EXEC meson --werror -Dtests=unsafe -Dslow-tests=true build
$DOCKER_EXEC ninja -v -C build
$DOCKER_EXEC ninja -C build test
- $DOCKER_EXEC tools/check-directives.sh
;;
RUN_CLANG)
- docker exec -e CC=clang -e CXX=clang++ -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true build
+ docker exec -e CC=clang -e CXX=clang++ -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dman=true build
$DOCKER_EXEC ninja -v -C build
$DOCKER_EXEC ninja -C build test
;;
--- /dev/null
+#!/bin/bash
+
+set -e
+set -x
+set -u
+
+REPO_ROOT=${REPO_ROOT:-$(pwd)}
+
+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 systemd -y
+sudo apt-get install -y ninja-build python3-pip python3-setuptools
+pip3 install meson
+
+cd $REPO_ROOT
+export PATH="$HOME/.local/bin/:$PATH"
+tools/oss-fuzz.sh
+timeout --preserve-status 5 ./out/fuzz-unit-file
+git clean -dxff
+
+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
+
+# docker doesn't like colons in filenames so let's create a directory
+# whose name can be consumed by the -v option.
+# https://github.com/google/oss-fuzz/issues/2428
+t=$(mktemp -d)
+sudo mount --bind "$REPO_ROOT" "$t"
+
+# 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 $t"
+sudo script -e -c "./infra/helper.py check_build --sanitizer=memory -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd"
set -e
set -x
+PACKAGES=(cryptsetup-bin
+ gettext
+ iptables-dev
+ iputils-ping
+ isc-dhcp-client
+ itstool
+ kbd
+ libblkid-dev
+ libcap-dev
+ libcurl4-gnutls-dev
+ libgpg-error-dev
+ liblz4-dev
+ liblzma-dev
+ libmicrohttpd-dev
+ libmount-dev
+ libmount-dev
+ libqrencode-dev
+ libxkbcommon-dev
+ linux-image-virtual
+ mount
+ net-tools
+ ninja-build
+ perl
+ python-lxml
+ python3-evdev
+ python3-lxml
+ python3-pip
+ python3-pyparsing
+ python3-setuptools
+ qemu-system-x86
+ strace
+ unifont
+ util-linux)
+
+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
+apt-get install -y "${PACKAGES[@]}"
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
+meson --werror -Db_sanitize=address,undefined -Dsplit-usr=true -Dman=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
dnf-plugins-core
meson
ninja-build
+perl
python
in_units = [
['debug-shell.service', ''],
['emergency.service', ''],
- ['halt-local.service', 'HAVE_SYSV_COMPAT'],
['initrd-cleanup.service', ''],
['initrd-parse-etc.service', ''],
['initrd-switch-root.service', ''],
# (at your option) any later version.
[Unit]
-Description=Remove the Offline System Updates symlink
+Description=Remove the Offline System Updates Symlink
Documentation=man:systemd.special(7) man:systemd.offline-updates(7)
After=system-update.target
DefaultDependencies=no
[Service]
ExecStart=@rootbindir@/journalctl --flush
+ExecStop=@rootbindir@/journalctl --smart-relinquish-var
Type=oneshot
RemainAfterExit=yes
TimeoutSec=90s